v1-0003-Use-a-compact-struct-to-deform-tuples.patch
application/octet-stream
Filename: v1-0003-Use-a-compact-struct-to-deform-tuples.patch
Type: application/octet-stream
Part: 4
Message:
Make tuple deformation faster
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 v1-0003
Subject: Use a compact struct to deform tuples
| File | + | − |
|---|---|---|
| contrib/amcheck/verify_heapam.c | 3 | 3 |
| contrib/pageinspect/heapfuncs.c | 2 | 2 |
| contrib/postgres_fdw/postgres_fdw.c | 6 | 6 |
| src/backend/access/brin/brin_inclusion.c | 9 | 9 |
| src/backend/access/brin/brin_tuple.c | 2 | 2 |
| src/backend/access/common/attmap.c | 9 | 5 |
| src/backend/access/common/heaptuple.c | 32 | 28 |
| src/backend/access/common/indextuple.c | 14 | 14 |
| src/backend/access/common/tupdesc.c | 52 | 6 |
| src/backend/access/gin/ginbulk.c | 3 | 3 |
| src/backend/access/gin/ginget.c | 4 | 4 |
| src/backend/access/gist/gistbuild.c | 4 | 2 |
| src/backend/access/heap/heapam.c | 5 | 5 |
| src/backend/access/heap/heapam_handler.c | 1 | 1 |
| src/backend/access/heap/heaptoast.c | 3 | 3 |
| src/backend/access/nbtree/nbtutils.c | 3 | 3 |
| src/backend/access/spgist/spgdoinsert.c | 1 | 1 |
| src/backend/access/table/toast_helper.c | 1 | 1 |
| src/backend/catalog/index.c | 2 | 0 |
| src/backend/commands/copy.c | 3 | 3 |
| src/backend/commands/tablecmds.c | 5 | 0 |
| src/backend/executor/execExpr.c | 4 | 4 |
| src/backend/executor/execExprInterp.c | 4 | 4 |
| src/backend/executor/execJunk.c | 1 | 1 |
| src/backend/executor/execTuples.c | 7 | 7 |
| src/backend/executor/functions.c | 2 | 1 |
| src/backend/executor/nodeMemoize.c | 6 | 6 |
| src/backend/executor/nodeModifyTable.c | 4 | 4 |
| src/backend/executor/nodeValuesscan.c | 2 | 2 |
| src/backend/executor/tstoreReceiver.c | 4 | 4 |
| src/backend/optimizer/util/plancat.c | 4 | 4 |
| src/backend/replication/pgoutput/pgoutput.c | 1 | 1 |
| src/backend/utils/adt/expandedrecord.c | 11 | 11 |
| src/backend/utils/adt/ri_triggers.c | 2 | 2 |
| src/backend/utils/cache/relcache.c | 12 | 0 |
| src/include/access/htup_details.h | 4 | 4 |
| src/include/access/itup.h | 7 | 4 |
| src/include/access/tupdesc.h | 49 | 3 |
| src/include/access/tupmacs.h | 2 | 0 |
From d022fc14b5c5a67dd538e2464d55c05c4a68dddd Mon Sep 17 00:00:00 2001
From: David Rowley <dgrowley@gmail.com>
Date: Tue, 28 May 2024 20:10:50 +1200
Subject: [PATCH v1 3/4] Use a compact struct to deform tuples
---
contrib/amcheck/verify_heapam.c | 6 +--
contrib/pageinspect/heapfuncs.c | 4 +-
contrib/postgres_fdw/postgres_fdw.c | 12 ++---
src/backend/access/brin/brin_inclusion.c | 18 +++----
src/backend/access/brin/brin_tuple.c | 4 +-
src/backend/access/common/attmap.c | 14 +++--
src/backend/access/common/heaptuple.c | 60 +++++++++++----------
src/backend/access/common/indextuple.c | 28 +++++-----
src/backend/access/common/tupdesc.c | 58 +++++++++++++++++---
src/backend/access/gin/ginbulk.c | 6 +--
src/backend/access/gin/ginget.c | 8 +--
src/backend/access/gist/gistbuild.c | 6 ++-
src/backend/access/heap/heapam.c | 10 ++--
src/backend/access/heap/heapam_handler.c | 2 +-
src/backend/access/heap/heaptoast.c | 6 +--
src/backend/access/nbtree/nbtutils.c | 6 +--
src/backend/access/spgist/spgdoinsert.c | 2 +-
src/backend/access/table/toast_helper.c | 2 +-
src/backend/catalog/index.c | 2 +
src/backend/commands/copy.c | 6 +--
src/backend/commands/tablecmds.c | 5 ++
src/backend/executor/execExpr.c | 8 +--
src/backend/executor/execExprInterp.c | 8 +--
src/backend/executor/execJunk.c | 2 +-
src/backend/executor/execTuples.c | 14 ++---
src/backend/executor/functions.c | 3 +-
src/backend/executor/nodeMemoize.c | 12 ++---
src/backend/executor/nodeModifyTable.c | 8 +--
src/backend/executor/nodeValuesscan.c | 4 +-
src/backend/executor/tstoreReceiver.c | 8 +--
src/backend/optimizer/util/plancat.c | 8 +--
src/backend/replication/pgoutput/pgoutput.c | 2 +-
src/backend/utils/adt/expandedrecord.c | 22 ++++----
src/backend/utils/adt/ri_triggers.c | 4 +-
src/backend/utils/cache/relcache.c | 12 +++++
src/include/access/htup_details.h | 8 +--
src/include/access/itup.h | 11 ++--
src/include/access/tupdesc.h | 52 ++++++++++++++++--
src/include/access/tupmacs.h | 2 +
39 files changed, 290 insertions(+), 163 deletions(-)
diff --git a/contrib/amcheck/verify_heapam.c b/contrib/amcheck/verify_heapam.c
index f2526ed63a..125d594826 100644
--- a/contrib/amcheck/verify_heapam.c
+++ b/contrib/amcheck/verify_heapam.c
@@ -1567,11 +1567,11 @@ check_tuple_attribute(HeapCheckContext *ctx)
struct varlena *attr;
char *tp; /* pointer to the tuple data */
uint16 infomask;
- Form_pg_attribute thisatt;
+ TupleDescDeformAttr *thisatt;
struct varatt_external toast_pointer;
infomask = ctx->tuphdr->t_infomask;
- thisatt = TupleDescAttr(RelationGetDescr(ctx->rel), ctx->attnum);
+ thisatt = TupleDescDeformAttr(RelationGetDescr(ctx->rel), ctx->attnum);
tp = (char *) ctx->tuphdr + ctx->tuphdr->t_hoff;
@@ -1612,7 +1612,7 @@ check_tuple_attribute(HeapCheckContext *ctx)
tp + ctx->offset);
/* Get the (possibly corrupt) varlena datum */
- attdatum = fetchatt(thisatt, tp + ctx->offset);
+ attdatum = fetchatt_fast(thisatt, tp + ctx->offset);
/*
* We have the datum, but we cannot decode it carelessly, as it may still
diff --git a/contrib/pageinspect/heapfuncs.c b/contrib/pageinspect/heapfuncs.c
index 3faeabc711..e610aa69c6 100644
--- a/contrib/pageinspect/heapfuncs.c
+++ b/contrib/pageinspect/heapfuncs.c
@@ -331,11 +331,11 @@ tuple_data_split_internal(Oid relid, char *tupdata,
for (i = 0; i < nattrs; i++)
{
- Form_pg_attribute attr;
+ TupleDescDeformAttr *attr;
bool is_null;
bytea *attr_data = NULL;
- attr = TupleDescAttr(tupdesc, i);
+ attr = TupleDescDeformAttr(tupdesc, i);
/*
* Tuple header can specify fewer attributes than tuple descriptor as
diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index 0bb9a5ae8f..10906a1363 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -1804,9 +1804,9 @@ postgresPlanForeignModify(PlannerInfo *root,
for (attnum = 1; attnum <= tupdesc->natts; attnum++)
{
- Form_pg_attribute attr = TupleDescAttr(tupdesc, attnum - 1);
+ TupleDescDeformAttr *attr = TupleDescDeformAttr(tupdesc, attnum - 1);
- if (!attr->attisdropped)
+ if (!DeformAttrIsDropped(attr))
targetAttrs = lappend_int(targetAttrs, attnum);
}
}
@@ -2177,9 +2177,9 @@ postgresBeginForeignInsert(ModifyTableState *mtstate,
/* We transmit all columns that are defined in the foreign table. */
for (attnum = 1; attnum <= tupdesc->natts; attnum++)
{
- Form_pg_attribute attr = TupleDescAttr(tupdesc, attnum - 1);
+ TupleDescDeformAttr *attr = TupleDescDeformAttr(tupdesc, attnum - 1);
- if (!attr->attisdropped)
+ if (!DeformAttrIsDropped(attr))
targetAttrs = lappend_int(targetAttrs, attnum);
}
@@ -4291,12 +4291,12 @@ convert_prep_stmt_params(PgFdwModifyState *fmstate,
foreach(lc, fmstate->target_attrs)
{
int attnum = lfirst_int(lc);
- Form_pg_attribute attr = TupleDescAttr(tupdesc, attnum - 1);
+ TupleDescDeformAttr *attr = TupleDescDeformAttr(tupdesc, attnum - 1);
Datum value;
bool isnull;
/* Ignore generated columns; they are set to DEFAULT */
- if (attr->attgenerated)
+ if (DeformAttrIsGenerated(attr))
continue;
value = slot_getattr(slots[i], attnum, &isnull);
if (isnull)
diff --git a/src/backend/access/brin/brin_inclusion.c b/src/backend/access/brin/brin_inclusion.c
index 750276998c..289418706d 100644
--- a/src/backend/access/brin/brin_inclusion.c
+++ b/src/backend/access/brin/brin_inclusion.c
@@ -146,12 +146,12 @@ brin_inclusion_add_value(PG_FUNCTION_ARGS)
Datum result;
bool new = false;
AttrNumber attno;
- Form_pg_attribute attr;
+ TupleDescDeformAttr *attr;
Assert(!isnull);
attno = column->bv_attno;
- attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1);
+ attr = TupleDescDeformAttr(bdesc->bd_tupdesc, attno - 1);
/*
* If the recorded value is null, copy the new value (which we know to be
@@ -160,7 +160,7 @@ brin_inclusion_add_value(PG_FUNCTION_ARGS)
if (column->bv_allnulls)
{
column->bv_values[INCLUSION_UNION] =
- datumCopy(newval, attr->attbyval, attr->attlen);
+ datumCopy(newval, DeformAttrByVal(attr), attr->attlen);
column->bv_values[INCLUSION_UNMERGEABLE] = BoolGetDatum(false);
column->bv_values[INCLUSION_CONTAINS_EMPTY] = BoolGetDatum(false);
column->bv_allnulls = false;
@@ -225,13 +225,13 @@ brin_inclusion_add_value(PG_FUNCTION_ARGS)
Assert(finfo != NULL);
result = FunctionCall2Coll(finfo, colloid,
column->bv_values[INCLUSION_UNION], newval);
- if (!attr->attbyval &&
+ if (!DeformAttrByVal(attr) &&
DatumGetPointer(result) != DatumGetPointer(column->bv_values[INCLUSION_UNION]))
{
pfree(DatumGetPointer(column->bv_values[INCLUSION_UNION]));
if (result == newval)
- result = datumCopy(result, attr->attbyval, attr->attlen);
+ result = datumCopy(result, DeformAttrByVal(attr), attr->attlen);
}
column->bv_values[INCLUSION_UNION] = result;
@@ -479,7 +479,7 @@ brin_inclusion_union(PG_FUNCTION_ARGS)
BrinValues *col_b = (BrinValues *) PG_GETARG_POINTER(2);
Oid colloid = PG_GET_COLLATION();
AttrNumber attno;
- Form_pg_attribute attr;
+ TupleDescDeformAttr *attr;
FmgrInfo *finfo;
Datum result;
@@ -487,7 +487,7 @@ brin_inclusion_union(PG_FUNCTION_ARGS)
Assert(!col_a->bv_allnulls && !col_b->bv_allnulls);
attno = col_a->bv_attno;
- attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1);
+ attr = TupleDescDeformAttr(bdesc->bd_tupdesc, attno - 1);
/* If B includes empty elements, mark A similarly, if needed. */
if (!DatumGetBool(col_a->bv_values[INCLUSION_CONTAINS_EMPTY]) &&
@@ -522,13 +522,13 @@ brin_inclusion_union(PG_FUNCTION_ARGS)
result = FunctionCall2Coll(finfo, colloid,
col_a->bv_values[INCLUSION_UNION],
col_b->bv_values[INCLUSION_UNION]);
- if (!attr->attbyval &&
+ if (!DeformAttrByVal(attr) &&
DatumGetPointer(result) != DatumGetPointer(col_a->bv_values[INCLUSION_UNION]))
{
pfree(DatumGetPointer(col_a->bv_values[INCLUSION_UNION]));
if (result == col_b->bv_values[INCLUSION_UNION])
- result = datumCopy(result, attr->attbyval, attr->attlen);
+ result = datumCopy(result, DeformAttrByVal(attr), attr->attlen);
}
col_a->bv_values[INCLUSION_UNION] = result;
diff --git a/src/backend/access/brin/brin_tuple.c b/src/backend/access/brin/brin_tuple.c
index 997eb6d822..213b873867 100644
--- a/src/backend/access/brin/brin_tuple.c
+++ b/src/backend/access/brin/brin_tuple.c
@@ -699,7 +699,7 @@ brin_deconstruct_tuple(BrinDesc *brdesc,
datumno < brdesc->bd_info[attnum]->oi_nstored;
datumno++)
{
- Form_pg_attribute thisatt = TupleDescAttr(diskdsc, stored);
+ TupleDescDeformAttr *thisatt = TupleDescDeformAttr(diskdsc, stored);
if (thisatt->attlen == -1)
{
@@ -712,7 +712,7 @@ brin_deconstruct_tuple(BrinDesc *brdesc,
off = att_align_nominal(off, thisatt->attalign);
}
- values[stored++] = fetchatt(thisatt, tp + off);
+ values[stored++] = fetchatt_fast(thisatt, tp + off);
off = att_addlength_pointer(off, thisatt->attlen, tp + off);
}
diff --git a/src/backend/access/common/attmap.c b/src/backend/access/common/attmap.c
index b0fe27ef57..a341f0bf50 100644
--- a/src/backend/access/common/attmap.c
+++ b/src/backend/access/common/attmap.c
@@ -135,7 +135,9 @@ build_attrmap_by_position(TupleDesc indesc,
/* Check for unused input columns */
for (; j < indesc->natts; j++)
{
- if (TupleDescAttr(indesc, j)->attisdropped)
+ TupleDescDeformAttr *attr = TupleDescDeformAttr(indesc, j);
+
+ if (DeformAttrIsDropped(attr))
continue;
nincols++;
same = false; /* we'll complain below */
@@ -299,25 +301,27 @@ check_attrmap_match(TupleDesc indesc,
for (i = 0; i < attrMap->maplen; i++)
{
- Form_pg_attribute inatt = TupleDescAttr(indesc, i);
- Form_pg_attribute outatt = TupleDescAttr(outdesc, i);
+ TupleDescDeformAttr *inatt = TupleDescDeformAttr(indesc, i);
+ TupleDescDeformAttr *outatt;
/*
* If the input column has a missing attribute, we need a conversion.
*/
- if (inatt->atthasmissing)
+ if (DeformAttrHasMissing(inatt))
return false;
if (attrMap->attnums[i] == (i + 1))
continue;
+ outatt = TupleDescDeformAttr(outdesc, i);
+
/*
* If it's a dropped column and the corresponding input column is also
* dropped, we don't need a conversion. However, attlen and attalign
* must agree.
*/
if (attrMap->attnums[i] == 0 &&
- inatt->attisdropped &&
+ DeformAttrIsDropped(inatt) &&
inatt->attlen == outatt->attlen &&
inatt->attalign == outatt->attalign)
continue;
diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c
index 9e3407bf98..963b88c27b 100644
--- a/src/backend/access/common/heaptuple.c
+++ b/src/backend/access/common/heaptuple.c
@@ -83,6 +83,9 @@
#define VARLENA_ATT_IS_PACKABLE(att) \
((att)->attstorage != TYPSTORAGE_PLAIN)
+#define ATT_IS_PACKABLE_FAST(att) \
+ ((att)->attlen == -1 && DeformAttrIsPackable(att))
+
/*
* Setup for caching pass-by-ref missing attributes in a way that survives
* tupleDesc destruction.
@@ -147,14 +150,14 @@ Datum
getmissingattr(TupleDesc tupleDesc,
int attnum, bool *isnull)
{
- Form_pg_attribute att;
+ TupleDescDeformAttr *att;
Assert(attnum <= tupleDesc->natts);
Assert(attnum > 0);
- att = TupleDescAttr(tupleDesc, attnum - 1);
+ att = TupleDescDeformAttr(tupleDesc, attnum - 1);
- if (att->atthasmissing)
+ if (DeformAttrHasMissing(att))
{
AttrMissing *attrmiss;
@@ -173,7 +176,7 @@ getmissingattr(TupleDesc tupleDesc,
*isnull = false;
/* no need to cache by-value attributes */
- if (att->attbyval)
+ if (DeformAttrByVal(att))
return attrmiss->am_value;
/* set up cache if required */
@@ -223,15 +226,15 @@ heap_compute_data_size(TupleDesc tupleDesc,
for (i = 0; i < numberOfAttributes; i++)
{
Datum val;
- Form_pg_attribute atti;
+ TupleDescDeformAttr *atti;
if (isnull[i])
continue;
val = values[i];
- atti = TupleDescAttr(tupleDesc, i);
+ atti = TupleDescDeformAttr(tupleDesc, i);
- if (ATT_IS_PACKABLE(atti) &&
+ if (ATT_IS_PACKABLE_FAST(atti) &&
VARATT_CAN_MAKE_SHORT(DatumGetPointer(val)))
{
/*
@@ -268,7 +271,7 @@ heap_compute_data_size(TupleDesc tupleDesc,
* Fill in either a data value or a bit in the null bitmask
*/
static inline void
-fill_val(Form_pg_attribute att,
+fill_val(TupleDescDeformAttr *att,
bits8 **bit,
int *bitmask,
char **dataP,
@@ -307,7 +310,7 @@ fill_val(Form_pg_attribute att,
* 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->attbyval)
+ if (DeformAttrByVal(att))
{
/* pass-by-value */
data = (char *) att_align_nominal(data, att->attalign);
@@ -349,7 +352,7 @@ fill_val(Form_pg_attribute att,
data_length = VARSIZE_SHORT(val);
memcpy(data, val, data_length);
}
- else if (VARLENA_ATT_IS_PACKABLE(att) &&
+ else if (DeformAttrIsPackable(att) &&
VARATT_CAN_MAKE_SHORT(val))
{
/* convert to short varlena -- no alignment */
@@ -427,7 +430,7 @@ heap_fill_tuple(TupleDesc tupleDesc,
for (i = 0; i < numberOfAttributes; i++)
{
- Form_pg_attribute attr = TupleDescAttr(tupleDesc, i);
+ TupleDescDeformAttr *attr = TupleDescDeformAttr(tupleDesc, i);
fill_val(attr,
bitP ? &bitP : NULL,
@@ -461,7 +464,8 @@ heap_attisnull(HeapTuple tup, int attnum, TupleDesc tupleDesc)
Assert(!tupleDesc || attnum <= tupleDesc->natts);
if (attnum > (int) HeapTupleHeaderGetNatts(tup->t_data))
{
- if (tupleDesc && TupleDescAttr(tupleDesc, attnum - 1)->atthasmissing)
+ if (tupleDesc &&
+ DeformAttrHasMissing(TupleDescDeformAttr(tupleDesc, attnum - 1)))
return false;
else
return true;
@@ -570,15 +574,15 @@ nocachegetattr(HeapTuple tup,
if (!slow)
{
- Form_pg_attribute att;
+ TupleDescDeformAttr *att;
/*
* 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.
*/
- att = TupleDescAttr(tupleDesc, attnum);
+ att = TupleDescDeformAttr(tupleDesc, attnum);
if (att->attcacheoff >= 0)
- return fetchatt(att, tp + att->attcacheoff);
+ return fetchatt_fast(att, tp + att->attcacheoff);
/*
* Otherwise, check for non-fixed-length attrs up to and including
@@ -591,7 +595,7 @@ nocachegetattr(HeapTuple tup,
for (j = 0; j <= attnum; j++)
{
- if (TupleDescAttr(tupleDesc, j)->attlen <= 0)
+ if (TupleDescDeformAttr(tupleDesc, j)->attlen <= 0)
{
slow = true;
break;
@@ -614,18 +618,18 @@ nocachegetattr(HeapTuple tup,
* fixed-width columns, in hope of avoiding future visits to this
* routine.
*/
- TupleDescAttr(tupleDesc, 0)->attcacheoff = 0;
+ TupleDescDeformAttr(tupleDesc, 0)->attcacheoff = 0;
/* we might have set some offsets in the slow path previously */
- while (j < natts && TupleDescAttr(tupleDesc, j)->attcacheoff > 0)
+ while (j < natts && TupleDescDeformAttr(tupleDesc, j)->attcacheoff > 0)
j++;
- off = TupleDescAttr(tupleDesc, j - 1)->attcacheoff +
- TupleDescAttr(tupleDesc, j - 1)->attlen;
+ off = TupleDescDeformAttr(tupleDesc, j - 1)->attcacheoff +
+ TupleDescDeformAttr(tupleDesc, j - 1)->attlen;
for (; j < natts; j++)
{
- Form_pg_attribute att = TupleDescAttr(tupleDesc, j);
+ TupleDescDeformAttr *att = TupleDescDeformAttr(tupleDesc, j);
if (att->attlen <= 0)
break;
@@ -639,7 +643,7 @@ nocachegetattr(HeapTuple tup,
Assert(j > attnum);
- off = TupleDescAttr(tupleDesc, attnum)->attcacheoff;
+ off = TupleDescDeformAttr(tupleDesc, attnum)->attcacheoff;
}
else
{
@@ -659,7 +663,7 @@ nocachegetattr(HeapTuple tup,
off = 0;
for (i = 0;; i++) /* loop exit is at "break" */
{
- Form_pg_attribute att = TupleDescAttr(tupleDesc, i);
+ TupleDescDeformAttr *att = TupleDescDeformAttr(tupleDesc, i);
if (HeapTupleHasNulls(tup) && att_isnull(i, bp))
{
@@ -707,7 +711,7 @@ nocachegetattr(HeapTuple tup,
}
}
- return fetchatt(TupleDescAttr(tupleDesc, attnum), tp + off);
+ return fetchatt_fast(TupleDescDeformAttr(tupleDesc, attnum), tp + off);
}
/* ----------------
@@ -892,7 +896,7 @@ expand_tuple(HeapTuple *targetHeapTuple,
{
if (attrmiss[attnum].am_present)
{
- Form_pg_attribute att = TupleDescAttr(tupleDesc, attnum);
+ TupleDescDeformAttr *att = TupleDescDeformAttr(tupleDesc, attnum);
targetDataLen = att_align_datum(targetDataLen,
att->attalign,
@@ -1021,7 +1025,7 @@ expand_tuple(HeapTuple *targetHeapTuple,
for (attnum = sourceNatts; attnum < natts; attnum++)
{
- Form_pg_attribute attr = TupleDescAttr(tupleDesc, attnum);
+ TupleDescDeformAttr *attr = TupleDescDeformAttr(tupleDesc, attnum);
if (attrmiss && attrmiss[attnum].am_present)
{
@@ -1370,7 +1374,7 @@ heap_deform_tuple(HeapTuple tuple, TupleDesc tupleDesc,
for (attnum = 0; attnum < natts; attnum++)
{
- Form_pg_attribute thisatt = TupleDescAttr(tupleDesc, attnum);
+ TupleDescDeformAttr *thisatt = TupleDescDeformAttr(tupleDesc, attnum);
if (hasnulls && att_isnull(attnum, bp))
{
@@ -1411,7 +1415,7 @@ heap_deform_tuple(HeapTuple tuple, TupleDesc tupleDesc,
thisatt->attcacheoff = off;
}
- values[attnum] = fetchatt(thisatt, tp + off);
+ values[attnum] = fetchatt_fast(thisatt, tp + off);
off = att_addlength_pointer(off, thisatt->attlen, tp + off);
diff --git a/src/backend/access/common/indextuple.c b/src/backend/access/common/indextuple.c
index 79ae29989d..aac4214cc7 100644
--- a/src/backend/access/common/indextuple.c
+++ b/src/backend/access/common/indextuple.c
@@ -303,15 +303,15 @@ nocache_index_getattr(IndexTuple tup,
if (!slow)
{
- Form_pg_attribute att;
+ TupleDescDeformAttr *att;
/*
* 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.
*/
- att = TupleDescAttr(tupleDesc, attnum);
+ att = TupleDescDeformAttr(tupleDesc, attnum);
if (att->attcacheoff >= 0)
- return fetchatt(att, tp + att->attcacheoff);
+ return fetchatt_fast(att, tp + att->attcacheoff);
/*
* Otherwise, check for non-fixed-length attrs up to and including
@@ -324,7 +324,7 @@ nocache_index_getattr(IndexTuple tup,
for (j = 0; j <= attnum; j++)
{
- if (TupleDescAttr(tupleDesc, j)->attlen <= 0)
+ if (TupleDescDeformAttr(tupleDesc, j)->attlen <= 0)
{
slow = true;
break;
@@ -347,18 +347,18 @@ nocache_index_getattr(IndexTuple tup,
* fixed-width columns, in hope of avoiding future visits to this
* routine.
*/
- TupleDescAttr(tupleDesc, 0)->attcacheoff = 0;
+ TupleDescDeformAttr(tupleDesc, 0)->attcacheoff = 0;
/* we might have set some offsets in the slow path previously */
- while (j < natts && TupleDescAttr(tupleDesc, j)->attcacheoff > 0)
+ while (j < natts && TupleDescDeformAttr(tupleDesc, j)->attcacheoff > 0)
j++;
- off = TupleDescAttr(tupleDesc, j - 1)->attcacheoff +
- TupleDescAttr(tupleDesc, j - 1)->attlen;
+ off = TupleDescDeformAttr(tupleDesc, j - 1)->attcacheoff +
+ TupleDescDeformAttr(tupleDesc, j - 1)->attlen;
for (; j < natts; j++)
{
- Form_pg_attribute att = TupleDescAttr(tupleDesc, j);
+ TupleDescDeformAttr *att = TupleDescDeformAttr(tupleDesc, j);
if (att->attlen <= 0)
break;
@@ -372,7 +372,7 @@ nocache_index_getattr(IndexTuple tup,
Assert(j > attnum);
- off = TupleDescAttr(tupleDesc, attnum)->attcacheoff;
+ off = TupleDescDeformAttr(tupleDesc, attnum)->attcacheoff;
}
else
{
@@ -392,7 +392,7 @@ nocache_index_getattr(IndexTuple tup,
off = 0;
for (i = 0;; i++) /* loop exit is at "break" */
{
- Form_pg_attribute att = TupleDescAttr(tupleDesc, i);
+ TupleDescDeformAttr *att = TupleDescDeformAttr(tupleDesc, i);
if (IndexTupleHasNulls(tup) && att_isnull(i, bp))
{
@@ -440,7 +440,7 @@ nocache_index_getattr(IndexTuple tup,
}
}
- return fetchatt(TupleDescAttr(tupleDesc, attnum), tp + off);
+ return fetchatt_fast(TupleDescDeformAttr(tupleDesc, attnum), tp + off);
}
/*
@@ -490,7 +490,7 @@ index_deform_tuple_internal(TupleDesc tupleDescriptor,
for (attnum = 0; attnum < natts; attnum++)
{
- Form_pg_attribute thisatt = TupleDescAttr(tupleDescriptor, attnum);
+ TupleDescDeformAttr *thisatt = TupleDescDeformAttr(tupleDescriptor, attnum);
if (hasnulls && att_isnull(attnum, bp))
{
@@ -531,7 +531,7 @@ index_deform_tuple_internal(TupleDesc tupleDescriptor,
thisatt->attcacheoff = off;
}
- values[attnum] = fetchatt(thisatt, tp + off);
+ values[attnum] = fetchatt_fast(thisatt, tp + off);
off = att_addlength_pointer(off, thisatt->attlen, tp + off);
diff --git a/src/backend/access/common/tupdesc.c b/src/backend/access/common/tupdesc.c
index fba0026520..ee996f465a 100644
--- a/src/backend/access/common/tupdesc.c
+++ b/src/backend/access/common/tupdesc.c
@@ -56,6 +56,30 @@ ResourceOwnerForgetTupleDesc(ResourceOwner owner, TupleDesc tupdesc)
ResourceOwnerForget(owner, PointerGetDatum(tupdesc), &tupdesc_resowner_desc);
}
+void
+populate_TupleDescAttr(TupleDescDeformAttr *dst, Form_pg_attribute src)
+{
+ dst->attcacheoff = -1;
+ dst->attlen = src->attlen;
+
+ dst->attflags = 0;
+
+ if (src->attbyval)
+ dst->attflags |= DEFORM_ATTR_FLAG_BYVAL;
+ if (src->attstorage != TYPSTORAGE_PLAIN)
+ dst->attflags |= DEFORM_ATTR_FLAG_IS_PACKABLE;
+ if (src->atthasmissing)
+ dst->attflags |= DEFORM_ATTR_FLAG_HAS_MISSING;
+ if (src->attisdropped)
+ dst->attflags |= DEFORM_ATTR_FLAG_IS_DROPPED;
+ if (src->attgenerated)
+ dst->attflags |= DEFORM_ATTR_FLAG_IS_GENERATED;
+ if (src->attnotnull)
+ dst->attflags |= DEFORM_ATTR_FLAG_IS_NOTNULL;
+
+ dst->attalign = src->attalign;
+}
+
/*
* CreateTemplateTupleDesc
* This function allocates an empty tuple descriptor structure.
@@ -85,7 +109,8 @@ CreateTemplateTupleDesc(int natts)
* could be less due to trailing padding, although with the current
* definition of pg_attribute there probably isn't any padding.
*/
- desc = (TupleDesc) palloc(MAXALIGN(sizeof(TupleDescData)) +
+ desc = (TupleDesc) palloc(offsetof(struct TupleDescData, deform_attrs) +
+ natts * sizeof(TupleDescDeformAttr) +
natts * sizeof(FormData_pg_attribute));
/*
@@ -118,8 +143,11 @@ CreateTupleDesc(int natts, Form_pg_attribute *attrs)
desc = CreateTemplateTupleDesc(natts);
for (i = 0; i < natts; ++i)
+ {
memcpy(TupleDescAttr(desc, i), attrs[i], ATTRIBUTE_FIXED_PART_SIZE);
-
+ populate_TupleDescAttr(TupleDescDeformAttr(desc, i),
+ TupleDescAttr(desc, i));
+ }
return desc;
}
@@ -156,6 +184,9 @@ CreateTupleDescCopy(TupleDesc tupdesc)
att->atthasmissing = false;
att->attidentity = '\0';
att->attgenerated = '\0';
+
+ populate_TupleDescAttr(TupleDescDeformAttr(desc, i),
+ TupleDescAttr(desc, i));
}
/* We can copy the tuple type identification, too */
@@ -184,6 +215,10 @@ CreateTupleDescCopyConstr(TupleDesc tupdesc)
TupleDescAttr(tupdesc, 0),
desc->natts * sizeof(FormData_pg_attribute));
+ for (i = 0; i < desc->natts; i++)
+ populate_TupleDescAttr(TupleDescDeformAttr(desc, i),
+ TupleDescAttr(desc, i));
+
/* Copy the TupleConstr data structure, if any */
if (constr)
{
@@ -208,10 +243,10 @@ CreateTupleDescCopyConstr(TupleDesc tupdesc)
{
if (constr->missing[i].am_present)
{
- Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
+ TupleDescDeformAttr *attr = TupleDescDeformAttr(tupdesc, i);
cpy->missing[i].am_value = datumCopy(constr->missing[i].am_value,
- attr->attbyval,
+ DeformAttrByVal(attr),
attr->attlen);
}
}
@@ -275,6 +310,9 @@ TupleDescCopy(TupleDesc dst, TupleDesc src)
att->atthasmissing = false;
att->attidentity = '\0';
att->attgenerated = '\0';
+
+ populate_TupleDescAttr(TupleDescDeformAttr(dst, i),
+ TupleDescAttr(dst, i));
}
dst->constr = NULL;
@@ -329,6 +367,8 @@ TupleDescCopyEntry(TupleDesc dst, AttrNumber dstAttno,
dstAtt->atthasmissing = false;
dstAtt->attidentity = '\0';
dstAtt->attgenerated = '\0';
+
+ populate_TupleDescAttr(TupleDescDeformAttr(dst, dstAttno - 1), dstAtt);
}
/*
@@ -528,10 +568,10 @@ equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2)
return false;
if (missval1->am_present)
{
- Form_pg_attribute missatt1 = TupleDescAttr(tupdesc1, i);
+ TupleDescDeformAttr *missatt1 = TupleDescDeformAttr(tupdesc1, i);
if (!datumIsEqual(missval1->am_value, missval2->am_value,
- missatt1->attbyval, missatt1->attlen))
+ DeformAttrByVal(missatt1), missatt1->attlen))
return false;
}
}
@@ -721,6 +761,9 @@ TupleDescInitEntry(TupleDesc desc,
att->attcompression = InvalidCompressionMethod;
att->attcollation = typeForm->typcollation;
+ populate_TupleDescAttr(TupleDescDeformAttr(desc, attributeNumber - 1),
+ att);
+
ReleaseSysCache(tuple);
}
@@ -828,6 +871,9 @@ TupleDescInitBuiltinEntry(TupleDesc desc,
default:
elog(ERROR, "unsupported type %u", oidtypeid);
}
+
+ populate_TupleDescAttr(TupleDescDeformAttr(desc, attributeNumber - 1),
+ att);
}
/*
diff --git a/src/backend/access/gin/ginbulk.c b/src/backend/access/gin/ginbulk.c
index 7f89cd5e82..c2c03261fa 100644
--- a/src/backend/access/gin/ginbulk.c
+++ b/src/backend/access/gin/ginbulk.c
@@ -127,11 +127,11 @@ ginInitBA(BuildAccumulator *accum)
static Datum
getDatumCopy(BuildAccumulator *accum, OffsetNumber attnum, Datum value)
{
- Form_pg_attribute att;
+ TupleDescDeformAttr *att;
Datum res;
- att = TupleDescAttr(accum->ginstate->origTupdesc, attnum - 1);
- if (att->attbyval)
+ att = TupleDescDeformAttr(accum->ginstate->origTupdesc, attnum - 1);
+ if (DeformAttrByVal(att))
res = value;
else
{
diff --git a/src/backend/access/gin/ginget.c b/src/backend/access/gin/ginget.c
index 0b4f2ebadb..d869cf2d77 100644
--- a/src/backend/access/gin/ginget.c
+++ b/src/backend/access/gin/ginget.c
@@ -122,7 +122,7 @@ collectMatchBitmap(GinBtreeData *btree, GinBtreeStack *stack,
GinScanEntry scanEntry, Snapshot snapshot)
{
OffsetNumber attnum;
- Form_pg_attribute attr;
+ TupleDescDeformAttr *attr;
/* Initialize empty bitmap result */
scanEntry->matchBitmap = tbm_create(work_mem * 1024L, NULL);
@@ -134,7 +134,7 @@ collectMatchBitmap(GinBtreeData *btree, GinBtreeStack *stack,
/* Locate tupdesc entry for key column (for attbyval/attlen data) */
attnum = scanEntry->attnum;
- attr = TupleDescAttr(btree->ginstate->origTupdesc, attnum - 1);
+ attr = TupleDescDeformAttr(btree->ginstate->origTupdesc, attnum - 1);
/*
* Predicate lock entry leaf page, following pages will be locked by
@@ -232,7 +232,7 @@ collectMatchBitmap(GinBtreeData *btree, GinBtreeStack *stack,
* tuple after re-locking
*/
if (icategory == GIN_CAT_NORM_KEY)
- idatum = datumCopy(idatum, attr->attbyval, attr->attlen);
+ idatum = datumCopy(idatum, DeformAttrByVal(attr), attr->attlen);
LockBuffer(stack->buffer, GIN_UNLOCK);
@@ -291,7 +291,7 @@ collectMatchBitmap(GinBtreeData *btree, GinBtreeStack *stack,
stack->off++;
}
- if (icategory == GIN_CAT_NORM_KEY && !attr->attbyval)
+ if (icategory == GIN_CAT_NORM_KEY && !DeformAttrByVal(attr))
pfree(DatumGetPointer(idatum));
}
else
diff --git a/src/backend/access/gist/gistbuild.c b/src/backend/access/gist/gistbuild.c
index ba06df30fa..0779948dbd 100644
--- a/src/backend/access/gist/gistbuild.c
+++ b/src/backend/access/gist/gistbuild.c
@@ -657,10 +657,12 @@ gistInitBuffering(GISTBuildState *buildstate)
itupMinSize = (Size) MAXALIGN(sizeof(IndexTupleData));
for (i = 0; i < index->rd_att->natts; i++)
{
- if (TupleDescAttr(index->rd_att, i)->attlen < 0)
+ TupleDescDeformAttr *attr = TupleDescDeformAttr(index->rd_att, i);
+
+ if (attr->attlen < 0)
itupMinSize += VARHDRSZ;
else
- itupMinSize += TupleDescAttr(index->rd_att, i)->attlen;
+ itupMinSize += attr->attlen;
}
/* Calculate average and maximal number of index tuples which fit to page */
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index 91b20147a0..205e601642 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -4080,8 +4080,6 @@ static bool
heap_attr_equals(TupleDesc tupdesc, int attrnum, Datum value1, Datum value2,
bool isnull1, bool isnull2)
{
- Form_pg_attribute att;
-
/*
* If one value is NULL and other is not, then they are certainly not
* equal
@@ -4111,9 +4109,11 @@ heap_attr_equals(TupleDesc tupdesc, int attrnum, Datum value1, Datum value2,
}
else
{
+ TupleDescDeformAttr *att;
+
Assert(attrnum <= tupdesc->natts);
- att = TupleDescAttr(tupdesc, attrnum - 1);
- return datumIsEqual(value1, value2, att->attbyval, att->attlen);
+ att = TupleDescDeformAttr(tupdesc, attrnum - 1);
+ return datumIsEqual(value1, value2, DeformAttrByVal(att), att->attlen);
}
}
@@ -4194,7 +4194,7 @@ HeapDetermineColumnsInfo(Relation relation,
* that system attributes can't be stored externally.
*/
if (attrnum < 0 || isnull1 ||
- TupleDescAttr(tupdesc, attrnum - 1)->attlen != -1)
+ TupleDescDeformAttr(tupdesc, attrnum - 1)->attlen != -1)
continue;
/*
diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c
index 6f8b1b7929..16d6a62ee1 100644
--- a/src/backend/access/heap/heapam_handler.c
+++ b/src/backend/access/heap/heapam_handler.c
@@ -2524,7 +2524,7 @@ reform_and_rewrite_tuple(HeapTuple tuple,
/* Be sure to null out any dropped columns */
for (i = 0; i < newTupDesc->natts; i++)
{
- if (TupleDescAttr(newTupDesc, i)->attisdropped)
+ if (DeformAttrIsDropped(TupleDescDeformAttr(newTupDesc, i)))
isnull[i] = true;
}
diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c
index a420e16530..ffc8d15d31 100644
--- a/src/backend/access/heap/heaptoast.c
+++ b/src/backend/access/heap/heaptoast.c
@@ -369,7 +369,7 @@ toast_flatten_tuple(HeapTuple tup, TupleDesc tupleDesc)
/*
* Look at non-null varlena attributes
*/
- if (!toast_isnull[i] && TupleDescAttr(tupleDesc, i)->attlen == -1)
+ if (!toast_isnull[i] && TupleDescDeformAttr(tupleDesc, i)->attlen == -1)
{
struct varlena *new_value;
@@ -483,7 +483,7 @@ toast_flatten_tuple_to_datum(HeapTupleHeader tup,
*/
if (toast_isnull[i])
has_nulls = true;
- else if (TupleDescAttr(tupleDesc, i)->attlen == -1)
+ else if (TupleDescDeformAttr(tupleDesc, i)->attlen == -1)
{
struct varlena *new_value;
@@ -584,7 +584,7 @@ toast_build_flattened_tuple(TupleDesc tupleDesc,
/*
* Look at non-null varlena attributes
*/
- if (!isnull[i] && TupleDescAttr(tupleDesc, i)->attlen == -1)
+ if (!isnull[i] && TupleDescDeformAttr(tupleDesc, i)->attlen == -1)
{
struct varlena *new_value;
diff --git a/src/backend/access/nbtree/nbtutils.c b/src/backend/access/nbtree/nbtutils.c
index d6de2072d4..bfc9daff41 100644
--- a/src/backend/access/nbtree/nbtutils.c
+++ b/src/backend/access/nbtree/nbtutils.c
@@ -4874,17 +4874,17 @@ _bt_keep_natts_fast(Relation rel, IndexTuple lastleft, IndexTuple firstright)
datum2;
bool isNull1,
isNull2;
- Form_pg_attribute att;
+ TupleDescDeformAttr *att;
datum1 = index_getattr(lastleft, attnum, itupdesc, &isNull1);
datum2 = index_getattr(firstright, attnum, itupdesc, &isNull2);
- att = TupleDescAttr(itupdesc, attnum - 1);
+ att = TupleDescDeformAttr(itupdesc, attnum - 1);
if (isNull1 != isNull2)
break;
if (!isNull1 &&
- !datum_image_eq(datum1, datum2, att->attbyval, att->attlen))
+ !datum_image_eq(datum1, datum2, DeformAttrByVal(att), att->attlen))
break;
keepnatts++;
diff --git a/src/backend/access/spgist/spgdoinsert.c b/src/backend/access/spgist/spgdoinsert.c
index a4995c168b..1cac593ec4 100644
--- a/src/backend/access/spgist/spgdoinsert.c
+++ b/src/backend/access/spgist/spgdoinsert.c
@@ -1974,7 +1974,7 @@ spgdoinsert(Relation index, SpGistState *state,
{
if (!isnulls[i])
{
- if (TupleDescAttr(leafDescriptor, i)->attlen == -1)
+ if (TupleDescDeformAttr(leafDescriptor, i)->attlen == -1)
leafDatums[i] = PointerGetDatum(PG_DETOAST_DATUM(datums[i]));
else
leafDatums[i] = datums[i];
diff --git a/src/backend/access/table/toast_helper.c b/src/backend/access/table/toast_helper.c
index 53224932f0..361f0cbd88 100644
--- a/src/backend/access/table/toast_helper.c
+++ b/src/backend/access/table/toast_helper.c
@@ -324,7 +324,7 @@ toast_delete_external(Relation rel, const Datum *values, const bool *isnull,
for (i = 0; i < numAttrs; i++)
{
- if (TupleDescAttr(tupleDesc, i)->attlen == -1)
+ if (TupleDescDeformAttr(tupleDesc, i)->attlen == -1)
{
Datum value = values[i];
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index a819b4197c..b59ba2b45c 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -477,6 +477,8 @@ ConstructTupleDescriptor(Relation heapRelation,
ReleaseSysCache(tuple);
}
+
+ populate_TupleDescAttr(TupleDescDeformAttr(indexTupDesc, i), to);
}
pfree(amroutine);
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index df7a4a21c9..ecd2044a15 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -867,9 +867,9 @@ CopyGetAttnums(TupleDesc tupDesc, Relation rel, List *attnamelist)
for (i = 0; i < attr_count; i++)
{
- if (TupleDescAttr(tupDesc, i)->attisdropped)
- continue;
- if (TupleDescAttr(tupDesc, i)->attgenerated)
+ TupleDescDeformAttr *attr = TupleDescDeformAttr(tupDesc, i);
+
+ if (DeformAttrIsDropped(attr) || DeformAttrIsGenerated(attr))
continue;
attnums = lappend_int(attnums, i + 1);
}
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 8fcb188323..96034cdefc 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -944,6 +944,9 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
cookedDefaults = lappend(cookedDefaults, cooked);
attr->atthasdef = true;
}
+
+ populate_TupleDescAttr(TupleDescDeformAttr(descriptor, attnum - 1),
+ attr);
}
/*
@@ -1350,6 +1353,8 @@ BuildDescForRelation(const List *columns)
att->attstorage = entry->storage;
else if (entry->storage_name)
att->attstorage = GetAttributeStorage(att->atttypid, entry->storage_name);
+
+ populate_TupleDescAttr(TupleDescDeformAttr(desc, attnum - 1), att);
}
if (has_not_null)
diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c
index ccd4863778..4d87186ce9 100644
--- a/src/backend/executor/execExpr.c
+++ b/src/backend/executor/execExpr.c
@@ -594,9 +594,9 @@ ExecBuildUpdateProjection(List *targetList,
*/
for (int attnum = relDesc->natts; attnum > 0; attnum--)
{
- Form_pg_attribute attr = TupleDescAttr(relDesc, attnum - 1);
+ TupleDescDeformAttr *attr = TupleDescDeformAttr(relDesc, attnum - 1);
- if (attr->attisdropped)
+ if (DeformAttrIsDropped(attr))
continue;
if (bms_is_member(attnum, assignedCols))
continue;
@@ -690,9 +690,9 @@ ExecBuildUpdateProjection(List *targetList,
*/
for (int attnum = 1; attnum <= relDesc->natts; attnum++)
{
- Form_pg_attribute attr = TupleDescAttr(relDesc, attnum - 1);
+ TupleDescDeformAttr *attr = TupleDescDeformAttr(relDesc, attnum - 1);
- if (attr->attisdropped)
+ if (DeformAttrIsDropped(attr))
{
/* Put a null into the ExprState's resvalue/resnull ... */
scratch.opcode = EEOP_CONST;
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index d8735286c4..22440cd10b 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -2798,7 +2798,7 @@ ExecEvalRowNullInt(ExprState *state, ExprEvalStep *op,
for (int att = 1; att <= tupDesc->natts; att++)
{
/* ignore dropped columns */
- if (TupleDescAttr(tupDesc, att - 1)->attisdropped)
+ if (DeformAttrIsDropped(TupleDescDeformAttr(tupDesc, att - 1)))
continue;
if (heap_attisnull(&tmptup, att, tupDesc))
{
@@ -4838,10 +4838,10 @@ ExecEvalWholeRowVar(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
for (int i = 0; i < var_tupdesc->natts; i++)
{
- Form_pg_attribute vattr = TupleDescAttr(var_tupdesc, i);
- Form_pg_attribute sattr = TupleDescAttr(tupleDesc, i);
+ TupleDescDeformAttr *vattr = TupleDescDeformAttr(var_tupdesc, i);
+ TupleDescDeformAttr *sattr = TupleDescDeformAttr(tupleDesc, i);
- if (!vattr->attisdropped)
+ if (!DeformAttrIsDropped(vattr))
continue; /* already checked non-dropped cols */
if (slot->tts_isnull[i])
continue; /* null is always okay */
diff --git a/src/backend/executor/execJunk.c b/src/backend/executor/execJunk.c
index b962c31383..bff4213a6c 100644
--- a/src/backend/executor/execJunk.c
+++ b/src/backend/executor/execJunk.c
@@ -169,7 +169,7 @@ ExecInitJunkFilterConversion(List *targetList,
t = list_head(targetList);
for (i = 0; i < cleanLength; i++)
{
- if (TupleDescAttr(cleanTupType, i)->attisdropped)
+ if (DeformAttrIsDropped(TupleDescDeformAttr(cleanTupType, i)))
continue; /* map entry is already zero */
for (;;)
{
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index 00dc339615..e651934918 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -187,10 +187,10 @@ tts_virtual_materialize(TupleTableSlot *slot)
/* compute size of memory required */
for (int natt = 0; natt < desc->natts; natt++)
{
- Form_pg_attribute att = TupleDescAttr(desc, natt);
+ TupleDescDeformAttr *att = TupleDescDeformAttr(desc, natt);
Datum val;
- if (att->attbyval || slot->tts_isnull[natt])
+ if (DeformAttrByVal(att) || slot->tts_isnull[natt])
continue;
val = slot->tts_values[natt];
@@ -223,10 +223,10 @@ tts_virtual_materialize(TupleTableSlot *slot)
/* and copy all attributes into the pre-allocated space */
for (int natt = 0; natt < desc->natts; natt++)
{
- Form_pg_attribute att = TupleDescAttr(desc, natt);
+ TupleDescDeformAttr *att = TupleDescDeformAttr(desc, natt);
Datum val;
- if (att->attbyval || slot->tts_isnull[natt])
+ if (DeformAttrByVal(att) || slot->tts_isnull[natt])
continue;
val = slot->tts_values[natt];
@@ -1044,7 +1044,7 @@ slot_deform_heap_tuple(TupleTableSlot *slot, HeapTuple tuple, uint32 *offp,
for (; attnum < natts; attnum++)
{
- Form_pg_attribute thisatt = TupleDescAttr(tupleDesc, attnum);
+ TupleDescDeformAttr *thisatt = TupleDescDeformAttr(tupleDesc, attnum);
if (hasnulls && att_isnull(attnum, bp))
{
@@ -1085,7 +1085,7 @@ slot_deform_heap_tuple(TupleTableSlot *slot, HeapTuple tuple, uint32 *offp,
thisatt->attcacheoff = off;
}
- values[attnum] = fetchatt(thisatt, tp + off);
+ values[attnum] = fetchatt_fast(thisatt, tp + off);
off = att_addlength_pointer(off, thisatt->attlen, tp + off);
@@ -2237,7 +2237,7 @@ BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values)
*/
for (i = 0; i < natts; i++)
{
- if (!TupleDescAttr(tupdesc, i)->attisdropped)
+ if (!DeformAttrIsDropped(TupleDescDeformAttr(tupdesc, i)))
{
/* Non-dropped attributes */
dvalues[i] = InputFunctionCall(&attinmeta->attinfuncs[i],
diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c
index 539cd0a999..7cd37fde34 100644
--- a/src/backend/executor/functions.c
+++ b/src/backend/executor/functions.c
@@ -1885,7 +1885,8 @@ check_sql_fn_retval(List *queryTreeLists,
/* remaining columns in rettupdesc had better all be dropped */
for (colindex++; colindex <= tupnatts; colindex++)
{
- if (!TupleDescAttr(rettupdesc, colindex - 1)->attisdropped)
+ if (!DeformAttrIsDropped(TupleDescDeformAttr(rettupdesc,
+ colindex - 1)))
ereport(ERROR,
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
errmsg("return type mismatch in function declared to return %s",
diff --git a/src/backend/executor/nodeMemoize.c b/src/backend/executor/nodeMemoize.c
index df8e3fff08..d73a4bb0c2 100644
--- a/src/backend/executor/nodeMemoize.c
+++ b/src/backend/executor/nodeMemoize.c
@@ -175,12 +175,12 @@ MemoizeHash_hash(struct memoize_hash *tb, const MemoizeKey *key)
if (!pslot->tts_isnull[i]) /* treat nulls as having hash key 0 */
{
- Form_pg_attribute attr;
+ TupleDescDeformAttr *attr;
uint32 hkey;
- attr = TupleDescAttr(pslot->tts_tupleDescriptor, i);
+ attr = TupleDescDeformAttr(pslot->tts_tupleDescriptor, i);
- hkey = datum_image_hash(pslot->tts_values[i], attr->attbyval, attr->attlen);
+ hkey = datum_image_hash(pslot->tts_values[i], DeformAttrByVal(attr), attr->attlen);
hashkey ^= hkey;
}
@@ -242,7 +242,7 @@ MemoizeHash_equal(struct memoize_hash *tb, const MemoizeKey *key1,
for (int i = 0; i < numkeys; i++)
{
- Form_pg_attribute attr;
+ TupleDescDeformAttr *attr;
if (tslot->tts_isnull[i] != pslot->tts_isnull[i])
{
@@ -255,9 +255,9 @@ MemoizeHash_equal(struct memoize_hash *tb, const MemoizeKey *key1,
continue;
/* perform binary comparison on the two datums */
- attr = TupleDescAttr(tslot->tts_tupleDescriptor, i);
+ attr = TupleDescDeformAttr(tslot->tts_tupleDescriptor, i);
if (!datum_image_eq(tslot->tts_values[i], pslot->tts_values[i],
- attr->attbyval, attr->attlen))
+ DeformAttrByVal(attr), attr->attlen))
{
match = false;
break;
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index a2442b7b0d..bf8d1e9702 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -495,14 +495,14 @@ ExecComputeStoredGenerated(ResultRelInfo *resultRelInfo,
for (int i = 0; i < natts; i++)
{
- Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
+ TupleDescDeformAttr *attr = TupleDescDeformAttr(tupdesc, i);
if (ri_GeneratedExprs[i])
{
Datum val;
bool isnull;
- Assert(attr->attgenerated == ATTRIBUTE_GENERATED_STORED);
+ Assert(TupleDescAttr(tupdesc, i)->attgenerated == ATTRIBUTE_GENERATED_STORED);
econtext->ecxt_scantuple = slot;
@@ -513,7 +513,7 @@ ExecComputeStoredGenerated(ResultRelInfo *resultRelInfo,
* memory for a pass-by-reference Datum is located.
*/
if (!isnull)
- val = datumCopy(val, attr->attbyval, attr->attlen);
+ val = datumCopy(val, DeformAttrByVal(attr), attr->attlen);
values[i] = val;
nulls[i] = isnull;
@@ -521,7 +521,7 @@ ExecComputeStoredGenerated(ResultRelInfo *resultRelInfo,
else
{
if (!nulls[i])
- values[i] = datumCopy(slot->tts_values[i], attr->attbyval, attr->attlen);
+ values[i] = datumCopy(slot->tts_values[i], DeformAttrByVal(attr), attr->attlen);
}
}
diff --git a/src/backend/executor/nodeValuesscan.c b/src/backend/executor/nodeValuesscan.c
index 92948917a0..2ab8c52ead 100644
--- a/src/backend/executor/nodeValuesscan.c
+++ b/src/backend/executor/nodeValuesscan.c
@@ -142,8 +142,8 @@ ValuesNext(ValuesScanState *node)
foreach(lc, exprstatelist)
{
ExprState *estate = (ExprState *) lfirst(lc);
- Form_pg_attribute attr = TupleDescAttr(slot->tts_tupleDescriptor,
- resind);
+ TupleDescDeformAttr *attr = TupleDescDeformAttr(slot->tts_tupleDescriptor,
+ resind);
values[resind] = ExecEvalExpr(estate,
econtext,
diff --git a/src/backend/executor/tstoreReceiver.c b/src/backend/executor/tstoreReceiver.c
index de4646b5c2..616530b5b6 100644
--- a/src/backend/executor/tstoreReceiver.c
+++ b/src/backend/executor/tstoreReceiver.c
@@ -65,9 +65,9 @@ tstoreStartupReceiver(DestReceiver *self, int operation, TupleDesc typeinfo)
{
for (i = 0; i < natts; i++)
{
- Form_pg_attribute attr = TupleDescAttr(typeinfo, i);
+ TupleDescDeformAttr *attr = TupleDescDeformAttr(typeinfo, i);
- if (attr->attisdropped)
+ if (DeformAttrIsDropped(attr))
continue;
if (attr->attlen == -1)
{
@@ -154,9 +154,9 @@ tstoreReceiveSlot_detoast(TupleTableSlot *slot, DestReceiver *self)
for (i = 0; i < natts; i++)
{
Datum val = slot->tts_values[i];
- Form_pg_attribute attr = TupleDescAttr(typeinfo, i);
+ TupleDescDeformAttr *attr = TupleDescDeformAttr(typeinfo, i);
- if (!attr->attisdropped && attr->attlen == -1 && !slot->tts_isnull[i])
+ if (!DeformAttrIsDropped(attr) && attr->attlen == -1 && !slot->tts_isnull[i])
{
if (VARATT_IS_EXTERNAL(DatumGetPointer(val)))
{
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index 9efdd844aa..5e1c452753 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -174,19 +174,19 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
{
for (int i = 0; i < relation->rd_att->natts; i++)
{
- Form_pg_attribute attr = TupleDescAttr(relation->rd_att, i);
+ TupleDescDeformAttr *attr = TupleDescDeformAttr(relation->rd_att, i);
- if (attr->attnotnull)
+ if (DeformAttrIsNotNull(attr))
{
rel->notnullattnums = bms_add_member(rel->notnullattnums,
- attr->attnum);
+ i + 1);
/*
* Per RemoveAttributeById(), dropped columns will have their
* attnotnull unset, so we needn't check for dropped columns
* in the above condition.
*/
- Assert(!attr->attisdropped);
+ Assert(!DeformAttrIsDropped(attr));
}
}
}
diff --git a/src/backend/replication/pgoutput/pgoutput.c b/src/backend/replication/pgoutput/pgoutput.c
index abef4eaf68..02d0211911 100644
--- a/src/backend/replication/pgoutput/pgoutput.c
+++ b/src/backend/replication/pgoutput/pgoutput.c
@@ -1305,7 +1305,7 @@ pgoutput_row_filter(Relation relation, TupleTableSlot *old_slot,
*/
for (i = 0; i < desc->natts; i++)
{
- Form_pg_attribute att = TupleDescAttr(desc, i);
+ TupleDescDeformAttr *att = TupleDescDeformAttr(desc, i);
/*
* if the column in the new tuple or old tuple is null, nothing to do
diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c
index 7e85ae3825..d0b28d48fb 100644
--- a/src/backend/utils/adt/expandedrecord.c
+++ b/src/backend/utils/adt/expandedrecord.c
@@ -699,10 +699,10 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
{
for (i = 0; i < erh->nfields; i++)
{
- Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
+ TupleDescDeformAttr *attr = TupleDescDeformAttr(tupdesc, i);
if (!erh->dnulls[i] &&
- !attr->attbyval && attr->attlen == -1 &&
+ !DeformAttrByVal(attr) && attr->attlen == -1 &&
VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])))
{
/*
@@ -1115,7 +1115,7 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber,
bool check_constraints)
{
TupleDesc tupdesc;
- Form_pg_attribute attr;
+ TupleDescDeformAttr *attr;
Datum *dvalues;
bool *dnulls;
char *oldValue;
@@ -1146,8 +1146,8 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber,
* Copy new field value into record's context, and deal with detoasting,
* if needed.
*/
- attr = TupleDescAttr(tupdesc, fnumber - 1);
- if (!isnull && !attr->attbyval)
+ attr = TupleDescDeformAttr(tupdesc, fnumber - 1);
+ if (!isnull && !DeformAttrByVal(attr))
{
MemoryContext oldcxt;
@@ -1201,7 +1201,7 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber,
erh->flat_size = 0;
/* Grab old field value for pfree'ing, if needed. */
- if (!attr->attbyval && !dnulls[fnumber - 1])
+ if (!DeformAttrByVal(attr) && !dnulls[fnumber - 1])
oldValue = (char *) DatumGetPointer(dvalues[fnumber - 1]);
else
oldValue = NULL;
@@ -1279,18 +1279,18 @@ expanded_record_set_fields(ExpandedRecordHeader *erh,
for (fnumber = 0; fnumber < erh->nfields; fnumber++)
{
- Form_pg_attribute attr = TupleDescAttr(tupdesc, fnumber);
+ TupleDescDeformAttr *attr = TupleDescDeformAttr(tupdesc, fnumber);
Datum newValue;
bool isnull;
/* Ignore dropped columns */
- if (attr->attisdropped)
+ if (DeformAttrIsDropped(attr))
continue;
newValue = newValues[fnumber];
isnull = isnulls[fnumber];
- if (!attr->attbyval)
+ if (!DeformAttrByVal(attr))
{
/*
* Copy new field value into record's context, and deal with
@@ -1541,9 +1541,9 @@ check_domain_for_new_field(ExpandedRecordHeader *erh, int fnumber,
*/
if (!isnull)
{
- Form_pg_attribute attr = TupleDescAttr(erh->er_tupdesc, fnumber - 1);
+ TupleDescDeformAttr *attr = TupleDescDeformAttr(erh->er_tupdesc, fnumber - 1);
- if (!attr->attbyval && attr->attlen == -1 &&
+ if (!DeformAttrByVal(attr) && attr->attlen == -1 &&
VARATT_IS_EXTERNAL(DatumGetPointer(newValue)))
dummy_erh->flags |= ER_FLAG_HAVE_EXTERNAL;
}
diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c
index 62601a6d80..f4637e466a 100644
--- a/src/backend/utils/adt/ri_triggers.c
+++ b/src/backend/utils/adt/ri_triggers.c
@@ -2833,9 +2833,9 @@ ri_KeysEqual(Relation rel, TupleTableSlot *oldslot, TupleTableSlot *newslot,
* difference for ON UPDATE CASCADE, but for consistency we treat
* all changes to the PK the same.
*/
- Form_pg_attribute att = TupleDescAttr(oldslot->tts_tupleDescriptor, attnums[i] - 1);
+ TupleDescDeformAttr *att = TupleDescDeformAttr(oldslot->tts_tupleDescriptor, attnums[i] - 1);
- if (!datum_image_eq(oldvalue, newvalue, att->attbyval, att->attlen))
+ if (!datum_image_eq(oldvalue, newvalue, DeformAttrByVal(att), att->attlen))
return false;
}
else
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 930cc03ee2..0b9a97d174 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -586,6 +586,10 @@ RelationBuildTupleDesc(Relation relation)
attp,
ATTRIBUTE_FIXED_PART_SIZE);
+ populate_TupleDescAttr(TupleDescDeformAttr(relation->rd_att,
+ attnum - 1),
+ attp);
+
/* Update constraint/default info */
if (attp->attnotnull)
constr->has_not_null = true;
@@ -1967,6 +1971,9 @@ formrdesc(const char *relationName, Oid relationReltype,
has_not_null |= attrs[i].attnotnull;
/* make sure attcacheoff is valid */
TupleDescAttr(relation->rd_att, i)->attcacheoff = -1;
+
+ populate_TupleDescAttr(TupleDescDeformAttr(relation->rd_att, i),
+ TupleDescAttr(relation->rd_att, i));
}
/* initialize first attribute's attcacheoff, cf RelationBuildTupleDesc */
@@ -4436,6 +4443,9 @@ BuildHardcodedDescriptor(int natts, const FormData_pg_attribute *attrs)
memcpy(TupleDescAttr(result, i), &attrs[i], ATTRIBUTE_FIXED_PART_SIZE);
/* make sure attcacheoff is valid */
TupleDescAttr(result, i)->attcacheoff = -1;
+
+ populate_TupleDescAttr(TupleDescDeformAttr(result, i),
+ TupleDescAttr(result, i));
}
/* initialize first attribute's attcacheoff, cf RelationBuildTupleDesc */
@@ -6174,6 +6184,8 @@ load_relcache_init_file(bool shared)
goto read_failed;
has_not_null |= attr->attnotnull;
+
+ populate_TupleDescAttr(TupleDescDeformAttr(rel->rd_att, i), attr);
}
/* next read the access method specific field */
diff --git a/src/include/access/htup_details.h b/src/include/access/htup_details.h
index 5e38ef8696..92f5882b27 100644
--- a/src/include/access/htup_details.h
+++ b/src/include/access/htup_details.h
@@ -758,12 +758,12 @@ fastgetattr(HeapTuple tup, int attnum, TupleDesc tupleDesc, bool *isnull)
*isnull = false;
if (HeapTupleNoNulls(tup))
{
- Form_pg_attribute att;
+ TupleDescDeformAttr *att;
- att = TupleDescAttr(tupleDesc, attnum - 1);
+ att = TupleDescDeformAttr(tupleDesc, attnum - 1);
if (att->attcacheoff >= 0)
- return fetchatt(att, (char *) tup->t_data + tup->t_data->t_hoff +
- att->attcacheoff);
+ return fetchatt_fast(att, (char *) tup->t_data + tup->t_data->t_hoff +
+ att->attcacheoff);
else
return nocachegetattr(tup, attnum, tupleDesc);
}
diff --git a/src/include/access/itup.h b/src/include/access/itup.h
index 94885751e5..f9d33e3bb1 100644
--- a/src/include/access/itup.h
+++ b/src/include/access/itup.h
@@ -117,18 +117,21 @@ IndexInfoFindDataOffset(unsigned short t_info)
static inline Datum
index_getattr(IndexTuple tup, int attnum, TupleDesc tupleDesc, bool *isnull)
{
+ TupleDescDeformAttr *attr;
+
Assert(PointerIsValid(isnull));
Assert(attnum > 0);
*isnull = false;
+ attr = TupleDescDeformAttr(tupleDesc, attnum - 1);
if (!IndexTupleHasNulls(tup))
{
- if (TupleDescAttr(tupleDesc, attnum - 1)->attcacheoff >= 0)
+ if (attr->attcacheoff >= 0)
{
- return fetchatt(TupleDescAttr(tupleDesc, attnum - 1),
- (char *) tup + IndexInfoFindDataOffset(tup->t_info)
- + TupleDescAttr(tupleDesc, attnum - 1)->attcacheoff);
+ return fetchatt_fast(attr,
+ (char *) tup + IndexInfoFindDataOffset(tup->t_info) +
+ attr->attcacheoff);
}
else
return nocache_index_getattr(tup, attnum, tupleDesc);
diff --git a/src/include/access/tupdesc.h b/src/include/access/tupdesc.h
index 2c435cdcb2..908c32254e 100644
--- a/src/include/access/tupdesc.h
+++ b/src/include/access/tupdesc.h
@@ -45,6 +45,43 @@ typedef struct TupleConstr
bool has_generated_stored;
} TupleConstr;
+/*
+ * Cut-down version of FormData_pg_attribute for faster access for tuple
+ * deformation.
+ */
+typedef struct TupleDescDeformAttr
+{
+ int32 attcacheoff;
+ int16 attlen;
+ uint8 attflags;
+ char attalign;
+} TupleDescDeformAttr;
+
+#define DEFORM_ATTR_FLAG_BYVAL (1 << 0)
+#define DEFORM_ATTR_FLAG_IS_PACKABLE (1 << 1)
+#define DEFORM_ATTR_FLAG_HAS_MISSING (1 << 2)
+#define DEFORM_ATTR_FLAG_IS_DROPPED (1 << 3)
+#define DEFORM_ATTR_FLAG_IS_GENERATED (1 << 4)
+#define DEFORM_ATTR_FLAG_IS_NOTNULL (1 << 5)
+
+#define DeformAttrByVal(att) \
+ (((att)->attflags & DEFORM_ATTR_FLAG_BYVAL) != 0)
+
+#define DeformAttrIsPackable(att) \
+ (((att)->attflags & DEFORM_ATTR_FLAG_IS_PACKABLE) != 0)
+
+#define DeformAttrHasMissing(att) \
+ (((att)->attflags & DEFORM_ATTR_FLAG_HAS_MISSING) != 0)
+
+#define DeformAttrIsDropped(att) \
+ (((att)->attflags & DEFORM_ATTR_FLAG_IS_DROPPED) != 0)
+
+#define DeformAttrIsGenerated(att) \
+ (((att)->attflags & DEFORM_ATTR_FLAG_IS_GENERATED) != 0)
+
+#define DeformAttrIsNotNull(att) \
+ (((att)->attflags & DEFORM_ATTR_FLAG_IS_NOTNULL) != 0)
+
/*
* This struct is passed around within the backend to describe the structure
* of tuples. For tuples coming from on-disk relations, the information is
@@ -85,12 +122,19 @@ typedef struct TupleDescData
TupleConstr *constr; /* constraints, or NULL if none */
/* attrs[N] is the description of Attribute Number N+1 */
FormData_pg_attribute *attrs;
+ TupleDescDeformAttr deform_attrs[FLEXIBLE_ARRAY_MEMBER];
} TupleDescData;
typedef struct TupleDescData *TupleDesc;
/* Accessor for the i'th attribute of tupdesc. */
#define TupleDescAttr(tupdesc, i) (&(tupdesc)->attrs[(i)])
+/* Accessor for the i'th TupleDescAttr of tupdesc */
+#define TupleDescDeformAttr(tupdesc, i) (&(tupdesc)->deform_attrs[(i)])
+
+extern void populate_TupleDescAttr(TupleDescDeformAttr *dst,
+ Form_pg_attribute src);
+
extern TupleDesc CreateTemplateTupleDesc(int natts);
extern TupleDesc CreateTupleDesc(int natts, Form_pg_attribute *attrs);
@@ -99,13 +143,15 @@ extern TupleDesc CreateTupleDescCopy(TupleDesc tupdesc);
extern TupleDesc CreateTupleDescCopyConstr(TupleDesc tupdesc);
-#define TupleDescSize(src) MAXALIGN(sizeof(TupleDescData))
+#define TupleDescSize(src) \
+ (offsetof(struct TupleDescData, deform_attrs) + \
+ (src)->natts * sizeof(TupleDescDeformAttr))
#define TupleDescFullSize(src) \
- (MAXALIGN(sizeof(TupleDescData)) + sizeof(FormData_pg_attribute) * (src)->natts)
+ (TupleDescSize(src) + sizeof(FormData_pg_attribute) * (src)->natts)
#define TupleDescAttrAddress(desc) \
- (Form_pg_attribute) ((char *) (desc) + MAXALIGN(sizeof(TupleDescData)))
+ (Form_pg_attribute) ((char *) (desc) + TupleDescSize(desc))
extern void TupleDescCopy(TupleDesc dst, TupleDesc src);
diff --git a/src/include/access/tupmacs.h b/src/include/access/tupmacs.h
index 58b3a58cfd..f96820f64a 100644
--- a/src/include/access/tupmacs.h
+++ b/src/include/access/tupmacs.h
@@ -45,6 +45,8 @@ att_isnull(int ATT, const bits8 *BITS)
*/
#define fetchatt(A,T) fetch_att(T, (A)->attbyval, (A)->attlen)
+#define fetchatt_fast(A, T) fetch_att(T, DeformAttrByVal(A), (A)->attlen)
+
/*
* Same, but work from byval/len parameters rather than Form_pg_attribute.
*/
--
2.34.1