Refactor att_align_nominal() to improve performance.

Separate att_align_nominal() into two macros, similarly to what
was already done with att_align_datum() and att_align_pointer().
The inner macro att_nominal_alignby() is really just TYPEALIGN(),
while att_align_nominal() retains its previous API by mapping
TYPALIGN_xxx values to numbers of bytes to align to and then
calling att_nominal_alignby().  In support of this, split out
tupdesc.c's logic to do that mapping into a publicly visible
function typalign_to_alignby().

Having done that, we can replace performance-critical uses of
att_align_nominal() with att_nominal_alignby(), where the
typalign_to_alignby() mapping is done just once outside the loop.

In most places I settled for doing typalign_to_alignby() once
per function.  We could in many places pass the alignby value
in from the caller if we wanted to change function APIs for this
purpose; but I'm a bit loath to do that, especially for exported
APIs that extensions might call.  Replacing a char typalign
argument by a uint8 typalignby argument would be an API change
that compilers would fail to warn about, thus silently breaking
code in hard-to-debug ways.  I did revise the APIs of array_iter_setup
and array_iter_next, moving the element type attribute arguments to
the former; if any external code uses those, the argument-count
change will cause visible compile failures.

Performance testing shows that ExecEvalScalarArrayOp is sped up by
about 10% by this change, when using a simple per-element function
such as int8eq.  I did not check any of the other loops optimized
here, but it's reasonable to expect similar gains.

Although the motivation for creating this patch was to avoid a
performance loss if we add some more typalign values, it evidently
is worth doing whether that patch lands or not.

Discussion: https://postgr.es/m/1127261.1769649624@sss.pgh.pa.us
This commit is contained in:
Tom Lane 2026-02-02 14:39:50 -05:00
parent 0c9f46c428
commit da7a1dc0d6
10 changed files with 166 additions and 119 deletions

View file

@ -2069,6 +2069,7 @@ get_text_array_contents(ArrayType *array, int *numitems)
int16 typlen;
bool typbyval;
char typalign;
uint8 typalignby;
char **values;
char *ptr;
bits8 *bitmap;
@ -2081,6 +2082,7 @@ get_text_array_contents(ArrayType *array, int *numitems)
get_typlenbyvalalign(ARR_ELEMTYPE(array),
&typlen, &typbyval, &typalign);
typalignby = typalign_to_alignby(typalign);
values = palloc_array(char *, nitems);
@ -2098,7 +2100,7 @@ get_text_array_contents(ArrayType *array, int *numitems)
{
values[i] = TextDatumGetCString(PointerGetDatum(ptr));
ptr = att_addlength_pointer(ptr, typlen, ptr);
ptr = (char *) att_align_nominal(ptr, typalign);
ptr = (char *) att_nominal_alignby(ptr, typalignby);
}
/* advance bitmap pointer if any */

View file

@ -86,25 +86,8 @@ populate_compact_attribute_internal(Form_pg_attribute src,
IsCatalogRelationOid(src->attrelid) ? ATTNULLABLE_VALID :
ATTNULLABLE_UNKNOWN;
switch (src->attalign)
{
case TYPALIGN_INT:
dst->attalignby = ALIGNOF_INT;
break;
case TYPALIGN_CHAR:
dst->attalignby = sizeof(char);
break;
case TYPALIGN_DOUBLE:
dst->attalignby = ALIGNOF_DOUBLE;
break;
case TYPALIGN_SHORT:
dst->attalignby = ALIGNOF_SHORT;
break;
default:
dst->attalignby = 0;
elog(ERROR, "invalid attalign value: %c", src->attalign);
break;
}
/* Compute numeric alignment requirement, too */
dst->attalignby = typalign_to_alignby(src->attalign);
}
/*

View file

@ -4032,6 +4032,7 @@ ExecEvalScalarArrayOp(ExprState *state, ExprEvalStep *op)
int16 typlen;
bool typbyval;
char typalign;
uint8 typalignby;
char *s;
bits8 *bitmap;
int bitmask;
@ -4086,6 +4087,7 @@ ExecEvalScalarArrayOp(ExprState *state, ExprEvalStep *op)
typlen = op->d.scalararrayop.typlen;
typbyval = op->d.scalararrayop.typbyval;
typalign = op->d.scalararrayop.typalign;
typalignby = typalign_to_alignby(typalign);
/* Initialize result appropriately depending on useOr */
result = BoolGetDatum(!useOr);
@ -4111,7 +4113,7 @@ ExecEvalScalarArrayOp(ExprState *state, ExprEvalStep *op)
{
elt = fetch_att(s, typbyval, typlen);
s = att_addlength_pointer(s, typlen, s);
s = (char *) att_align_nominal(s, typalign);
s = (char *) att_nominal_alignby(s, typalignby);
fcinfo->args[1].value = elt;
fcinfo->args[1].isnull = false;
}
@ -4255,6 +4257,7 @@ ExecEvalHashedScalarArrayOp(ExprState *state, ExprEvalStep *op, ExprContext *eco
int16 typlen;
bool typbyval;
char typalign;
uint8 typalignby;
int nitems;
bool has_nulls = false;
char *s;
@ -4272,6 +4275,7 @@ ExecEvalHashedScalarArrayOp(ExprState *state, ExprEvalStep *op, ExprContext *eco
&typlen,
&typbyval,
&typalign);
typalignby = typalign_to_alignby(typalign);
oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
@ -4318,7 +4322,7 @@ ExecEvalHashedScalarArrayOp(ExprState *state, ExprEvalStep *op, ExprContext *eco
element = fetch_att(s, typbyval, typlen);
s = att_addlength_pointer(s, typlen, s);
s = (char *) att_align_nominal(s, typalign);
s = (char *) att_nominal_alignby(s, typalignby);
saophash_insert(elements_tab->hashtab, element, &hashfound);
}

View file

@ -238,6 +238,7 @@ EA_get_flat_size(ExpandedObjectHeader *eohptr)
Datum *dvalues;
bool *dnulls;
Size nbytes;
uint8 typalignby;
int i;
Assert(eah->ea_magic == EA_MAGIC);
@ -261,12 +262,13 @@ EA_get_flat_size(ExpandedObjectHeader *eohptr)
dvalues = eah->dvalues;
dnulls = eah->dnulls;
nbytes = 0;
typalignby = typalign_to_alignby(eah->typalign);
for (i = 0; i < nelems; i++)
{
if (dnulls && dnulls[i])
continue;
nbytes = att_addlength_datum(nbytes, eah->typlen, dvalues[i]);
nbytes = att_align_nominal(nbytes, eah->typalign);
nbytes = att_nominal_alignby(nbytes, typalignby);
/* check for overflow of total request */
if (!AllocSizeIsValid(nbytes))
ereport(ERROR,

View file

@ -75,6 +75,7 @@ typedef struct ArrayIteratorData
int16 typlen; /* element type's length */
bool typbyval; /* element type's byval property */
char typalign; /* element type's align property */
uint8 typalignby; /* typalign mapped to numeric alignment */
/* information about the requested slice size */
int slice_ndim; /* slice dimension, or 0 if not slicing */
@ -123,7 +124,7 @@ static bool array_get_isnull(const bits8 *nullbitmap, int offset);
static void array_set_isnull(bits8 *nullbitmap, int offset, bool isNull);
static Datum ArrayCast(char *value, bool byval, int len);
static int ArrayCastAndSet(Datum src,
int typlen, bool typbyval, char typalign,
int typlen, bool typbyval, uint8 typalignby,
char *dest);
static char *array_seek(char *ptr, int offset, bits8 *nullbitmap, int nitems,
int typlen, bool typbyval, char typalign);
@ -187,6 +188,7 @@ array_in(PG_FUNCTION_ARGS)
int typlen;
bool typbyval;
char typalign;
uint8 typalignby;
char typdelim;
Oid typioparam;
char *p;
@ -232,6 +234,7 @@ array_in(PG_FUNCTION_ARGS)
typlen = my_extra->typlen;
typbyval = my_extra->typbyval;
typalign = my_extra->typalign;
typalignby = typalign_to_alignby(typalign);
typdelim = my_extra->typdelim;
typioparam = my_extra->typioparam;
@ -328,7 +331,7 @@ array_in(PG_FUNCTION_ARGS)
if (typlen == -1)
values[i] = PointerGetDatum(PG_DETOAST_DATUM(values[i]));
nbytes = att_addlength_datum(nbytes, typlen, values[i]);
nbytes = att_align_nominal(nbytes, typalign);
nbytes = att_nominal_alignby(nbytes, typalignby);
/* check for overflow of total request */
if (!AllocSizeIsValid(nbytes))
ereturn(escontext, (Datum) 0,
@ -972,6 +975,7 @@ CopyArrayEls(ArrayType *array,
bits8 *bitmap = ARR_NULLBITMAP(array);
int bitval = 0;
int bitmask = 1;
uint8 typalignby = typalign_to_alignby(typalign);
int i;
if (typbyval)
@ -988,7 +992,7 @@ CopyArrayEls(ArrayType *array,
else
{
bitval |= bitmask;
p += ArrayCastAndSet(values[i], typlen, typbyval, typalign, p);
p += ArrayCastAndSet(values[i], typlen, typbyval, typalignby, p);
if (freedata)
pfree(DatumGetPointer(values[i]));
}
@ -1112,7 +1116,7 @@ array_out(PG_FUNCTION_ARGS)
needquotes = (bool *) palloc(nitems * sizeof(bool));
overall_length = 0;
array_iter_setup(&iter, v);
array_iter_setup(&iter, v, typlen, typbyval, typalign);
for (i = 0; i < nitems; i++)
{
@ -1121,8 +1125,7 @@ array_out(PG_FUNCTION_ARGS)
bool needquote;
/* Get source element, checking for NULL */
itemvalue = array_iter_next(&iter, &isnull, i,
typlen, typbyval, typalign);
itemvalue = array_iter_next(&iter, &isnull, i);
if (isnull)
{
@ -1468,6 +1471,7 @@ ReadArrayBinary(StringInfo buf,
int i;
bool hasnull;
int32 totbytes;
uint8 typalignby = typalign_to_alignby(typalign);
for (i = 0; i < nitems; i++)
{
@ -1526,7 +1530,7 @@ ReadArrayBinary(StringInfo buf,
if (typlen == -1)
values[i] = PointerGetDatum(PG_DETOAST_DATUM(values[i]));
totbytes = att_addlength_datum(totbytes, typlen, values[i]);
totbytes = att_align_nominal(totbytes, typalign);
totbytes = att_nominal_alignby(totbytes, typalignby);
/* check for overflow of total request */
if (!AllocSizeIsValid(totbytes))
ereport(ERROR,
@ -1614,7 +1618,7 @@ array_send(PG_FUNCTION_ARGS)
}
/* Send the array elements using the element's own sendproc */
array_iter_setup(&iter, v);
array_iter_setup(&iter, v, typlen, typbyval, typalign);
for (i = 0; i < nitems; i++)
{
@ -1622,8 +1626,7 @@ array_send(PG_FUNCTION_ARGS)
bool isnull;
/* Get source element, checking for NULL */
itemvalue = array_iter_next(&iter, &isnull, i,
typlen, typbyval, typalign);
itemvalue = array_iter_next(&iter, &isnull, i);
if (isnull)
{
@ -2231,6 +2234,7 @@ array_set_element(Datum arraydatum,
addedafter,
lenbefore,
lenafter;
uint8 elmalignby = typalign_to_alignby(elmalign);
if (arraytyplen > 0)
{
@ -2258,7 +2262,7 @@ array_set_element(Datum arraydatum,
resultarray = (char *) palloc(arraytyplen);
memcpy(resultarray, DatumGetPointer(arraydatum), arraytyplen);
elt_ptr = resultarray + indx[0] * elmlen;
ArrayCastAndSet(dataValue, elmlen, elmbyval, elmalign, elt_ptr);
ArrayCastAndSet(dataValue, elmlen, elmbyval, elmalignby, elt_ptr);
return PointerGetDatum(resultarray);
}
@ -2416,7 +2420,7 @@ array_set_element(Datum arraydatum,
else
{
olditemlen = att_addlength_pointer(0, elmlen, elt_ptr);
olditemlen = att_align_nominal(olditemlen, elmalign);
olditemlen = att_nominal_alignby(olditemlen, elmalignby);
}
lenafter = olddatasize - lenbefore - olditemlen;
}
@ -2426,7 +2430,7 @@ array_set_element(Datum arraydatum,
else
{
newitemlen = att_addlength_datum(0, elmlen, dataValue);
newitemlen = att_align_nominal(newitemlen, elmalign);
newitemlen = att_nominal_alignby(newitemlen, elmalignby);
}
newsize = overheadlen + lenbefore + newitemlen + lenafter;
@ -2449,7 +2453,7 @@ array_set_element(Datum arraydatum,
(char *) array + oldoverheadlen,
lenbefore);
if (!isNull)
ArrayCastAndSet(dataValue, elmlen, elmbyval, elmalign,
ArrayCastAndSet(dataValue, elmlen, elmbyval, elmalignby,
(char *) newarray + overheadlen + lenbefore);
memcpy((char *) newarray + overheadlen + lenbefore + newitemlen,
(char *) array + oldoverheadlen + lenbefore + olditemlen,
@ -3221,6 +3225,7 @@ array_map(Datum arrayd,
int typlen;
bool typbyval;
char typalign;
uint8 typalignby;
array_iter iter;
ArrayMetaState *inp_extra;
ArrayMetaState *ret_extra;
@ -3270,21 +3275,21 @@ array_map(Datum arrayd,
typlen = ret_extra->typlen;
typbyval = ret_extra->typbyval;
typalign = ret_extra->typalign;
typalignby = typalign_to_alignby(typalign);
/* Allocate temporary arrays for new values */
values = (Datum *) palloc(nitems * sizeof(Datum));
nulls = (bool *) palloc(nitems * sizeof(bool));
/* Loop over source data */
array_iter_setup(&iter, v);
array_iter_setup(&iter, v, inp_typlen, inp_typbyval, inp_typalign);
hasnulls = false;
for (i = 0; i < nitems; i++)
{
/* Get source element, checking for NULL */
*transform_source =
array_iter_next(&iter, transform_source_isnull, i,
inp_typlen, inp_typbyval, inp_typalign);
array_iter_next(&iter, transform_source_isnull, i);
/* Apply the given expression to source element */
values[i] = ExecEvalExpr(exprstate, econtext, &nulls[i]);
@ -3298,7 +3303,7 @@ array_map(Datum arrayd,
values[i] = PointerGetDatum(PG_DETOAST_DATUM(values[i]));
/* Update total result size */
nbytes = att_addlength_datum(nbytes, typlen, values[i]);
nbytes = att_align_nominal(nbytes, typalign);
nbytes = att_nominal_alignby(nbytes, typalignby);
/* check for overflow of total request */
if (!AllocSizeIsValid(nbytes))
ereport(ERROR,
@ -3505,6 +3510,7 @@ construct_md_array(Datum *elems,
int32 dataoffset;
int i;
int nelems;
uint8 elmalignby = typalign_to_alignby(elmalign);
if (ndims < 0) /* we do allow zero-dimension arrays */
ereport(ERROR,
@ -3538,7 +3544,7 @@ construct_md_array(Datum *elems,
if (elmlen == -1)
elems[i] = PointerGetDatum(PG_DETOAST_DATUM(elems[i]));
nbytes = att_addlength_datum(nbytes, elmlen, elems[i]);
nbytes = att_align_nominal(nbytes, elmalign);
nbytes = att_nominal_alignby(nbytes, elmalignby);
/* check for overflow of total request */
if (!AllocSizeIsValid(nbytes))
ereport(ERROR,
@ -3641,6 +3647,7 @@ deconstruct_array(const ArrayType *array,
bits8 *bitmap;
int bitmask;
int i;
uint8 elmalignby = typalign_to_alignby(elmalign);
Assert(ARR_ELEMTYPE(array) == elmtype);
@ -3673,7 +3680,7 @@ deconstruct_array(const ArrayType *array,
{
elems[i] = fetch_att(p, elmbyval, elmlen);
p = att_addlength_pointer(p, elmlen, p);
p = (char *) att_align_nominal(p, elmalign);
p = (char *) att_nominal_alignby(p, elmalignby);
}
/* advance bitmap pointer if any */
@ -3878,8 +3885,8 @@ array_eq(PG_FUNCTION_ARGS)
/* Loop over source data */
nitems = ArrayGetNItems(ndims1, dims1);
array_iter_setup(&it1, array1);
array_iter_setup(&it2, array2);
array_iter_setup(&it1, array1, typlen, typbyval, typalign);
array_iter_setup(&it2, array2, typlen, typbyval, typalign);
for (i = 0; i < nitems; i++)
{
@ -3890,10 +3897,8 @@ array_eq(PG_FUNCTION_ARGS)
bool oprresult;
/* Get elements, checking for NULL */
elt1 = array_iter_next(&it1, &isnull1, i,
typlen, typbyval, typalign);
elt2 = array_iter_next(&it2, &isnull2, i,
typlen, typbyval, typalign);
elt1 = array_iter_next(&it1, &isnull1, i);
elt2 = array_iter_next(&it2, &isnull2, i);
/*
* We consider two NULLs equal; NULL and not-NULL are unequal.
@ -4042,8 +4047,8 @@ array_cmp(FunctionCallInfo fcinfo)
/* Loop over source data */
min_nitems = Min(nitems1, nitems2);
array_iter_setup(&it1, array1);
array_iter_setup(&it2, array2);
array_iter_setup(&it1, array1, typlen, typbyval, typalign);
array_iter_setup(&it2, array2, typlen, typbyval, typalign);
for (i = 0; i < min_nitems; i++)
{
@ -4054,8 +4059,8 @@ array_cmp(FunctionCallInfo fcinfo)
int32 cmpresult;
/* Get elements, checking for NULL */
elt1 = array_iter_next(&it1, &isnull1, i, typlen, typbyval, typalign);
elt2 = array_iter_next(&it2, &isnull2, i, typlen, typbyval, typalign);
elt1 = array_iter_next(&it1, &isnull1, i);
elt2 = array_iter_next(&it2, &isnull2, i);
/*
* We consider two NULLs equal; NULL > not-NULL.
@ -4238,7 +4243,7 @@ hash_array(PG_FUNCTION_ARGS)
/* Loop over source data */
nitems = ArrayGetNItems(ndims, dims);
array_iter_setup(&iter, array);
array_iter_setup(&iter, array, typlen, typbyval, typalign);
for (i = 0; i < nitems; i++)
{
@ -4247,7 +4252,7 @@ hash_array(PG_FUNCTION_ARGS)
uint32 elthash;
/* Get element, checking for NULL */
elt = array_iter_next(&iter, &isnull, i, typlen, typbyval, typalign);
elt = array_iter_next(&iter, &isnull, i);
if (isnull)
{
@ -4328,7 +4333,7 @@ hash_array_extended(PG_FUNCTION_ARGS)
/* Loop over source data */
nitems = ArrayGetNItems(ndims, dims);
array_iter_setup(&iter, array);
array_iter_setup(&iter, array, typlen, typbyval, typalign);
for (i = 0; i < nitems; i++)
{
@ -4337,7 +4342,7 @@ hash_array_extended(PG_FUNCTION_ARGS)
uint64 elthash;
/* Get element, checking for NULL */
elt = array_iter_next(&iter, &isnull, i, typlen, typbyval, typalign);
elt = array_iter_next(&iter, &isnull, i);
if (isnull)
{
@ -4451,7 +4456,7 @@ array_contain_compare(AnyArrayType *array1, AnyArrayType *array2, Oid collation,
/* Loop over source data */
nelems1 = ArrayGetNItems(AARR_NDIM(array1), AARR_DIMS(array1));
array_iter_setup(&it1, array1);
array_iter_setup(&it1, array1, typlen, typbyval, typalign);
for (i = 0; i < nelems1; i++)
{
@ -4459,7 +4464,7 @@ array_contain_compare(AnyArrayType *array1, AnyArrayType *array2, Oid collation,
bool isnull1;
/* Get element, checking for NULL */
elt1 = array_iter_next(&it1, &isnull1, i, typlen, typbyval, typalign);
elt1 = array_iter_next(&it1, &isnull1, i);
/*
* We assume that the comparison operator is strict, so a NULL can't
@ -4626,6 +4631,7 @@ array_create_iterator(ArrayType *arr, int slice_ndim, ArrayMetaState *mstate)
&iterator->typlen,
&iterator->typbyval,
&iterator->typalign);
iterator->typalignby = typalign_to_alignby(iterator->typalign);
/*
* Remember the slicing parameters.
@ -4700,7 +4706,7 @@ array_iterate(ArrayIterator iterator, Datum *value, bool *isnull)
/* Move our data pointer forward to the next element */
p = att_addlength_pointer(p, iterator->typlen, p);
p = (char *) att_align_nominal(p, iterator->typalign);
p = (char *) att_nominal_alignby(p, iterator->typalignby);
iterator->data_ptr = p;
}
}
@ -4730,7 +4736,7 @@ array_iterate(ArrayIterator iterator, Datum *value, bool *isnull)
/* Move our data pointer forward to the next element */
p = att_addlength_pointer(p, iterator->typlen, p);
p = (char *) att_align_nominal(p, iterator->typalign);
p = (char *) att_nominal_alignby(p, iterator->typalignby);
}
}
@ -4828,7 +4834,7 @@ static int
ArrayCastAndSet(Datum src,
int typlen,
bool typbyval,
char typalign,
uint8 typalignby,
char *dest)
{
int inc;
@ -4839,14 +4845,14 @@ ArrayCastAndSet(Datum src,
store_att_byval(dest, src, typlen);
else
memmove(dest, DatumGetPointer(src), typlen);
inc = att_align_nominal(typlen, typalign);
inc = att_nominal_alignby(typlen, typalignby);
}
else
{
Assert(!typbyval);
inc = att_addlength_datum(0, typlen, src);
memmove(dest, DatumGetPointer(src), inc);
inc = att_align_nominal(inc, typalign);
inc = att_nominal_alignby(inc, typalignby);
}
return inc;
@ -4867,12 +4873,13 @@ static char *
array_seek(char *ptr, int offset, bits8 *nullbitmap, int nitems,
int typlen, bool typbyval, char typalign)
{
uint8 typalignby = typalign_to_alignby(typalign);
int bitmask;
int i;
/* easy if fixed-size elements and no NULLs */
if (typlen > 0 && !nullbitmap)
return ptr + nitems * ((Size) att_align_nominal(typlen, typalign));
return ptr + nitems * ((Size) att_nominal_alignby(typlen, typalignby));
/* seems worth having separate loops for NULL and no-NULLs cases */
if (nullbitmap)
@ -4885,7 +4892,7 @@ array_seek(char *ptr, int offset, bits8 *nullbitmap, int nitems,
if (*nullbitmap & bitmask)
{
ptr = att_addlength_pointer(ptr, typlen, ptr);
ptr = (char *) att_align_nominal(ptr, typalign);
ptr = (char *) att_nominal_alignby(ptr, typalignby);
}
bitmask <<= 1;
if (bitmask == 0x100)
@ -4900,7 +4907,7 @@ array_seek(char *ptr, int offset, bits8 *nullbitmap, int nitems,
for (i = 0; i < nitems; i++)
{
ptr = att_addlength_pointer(ptr, typlen, ptr);
ptr = (char *) att_align_nominal(ptr, typalign);
ptr = (char *) att_nominal_alignby(ptr, typalignby);
}
}
return ptr;
@ -5050,12 +5057,13 @@ array_slice_size(char *arraydataptr, bits8 *arraynullsptr,
j,
inc;
int count = 0;
uint8 typalignby = typalign_to_alignby(typalign);
mda_get_range(ndim, span, st, endp);
/* Pretty easy for fixed element length without nulls ... */
if (typlen > 0 && !arraynullsptr)
return ArrayGetNItems(ndim, span) * att_align_nominal(typlen, typalign);
return ArrayGetNItems(ndim, span) * att_nominal_alignby(typlen, typalignby);
/* Else gotta do it the hard way */
src_offset = ArrayGetOffset(ndim, dim, lb, st);
@ -5077,7 +5085,7 @@ array_slice_size(char *arraydataptr, bits8 *arraynullsptr,
if (!array_get_isnull(arraynullsptr, src_offset))
{
inc = att_addlength_pointer(0, typlen, ptr);
inc = att_align_nominal(inc, typalign);
inc = att_nominal_alignby(inc, typalignby);
ptr += inc;
count += inc;
}
@ -6096,6 +6104,7 @@ array_fill_internal(ArrayType *dims, ArrayType *lbs,
int16 elmlen;
bool elmbyval;
char elmalign;
uint8 elmalignby;
ArrayMetaState *my_extra;
/*
@ -6190,6 +6199,7 @@ array_fill_internal(ArrayType *dims, ArrayType *lbs,
elmlen = my_extra->typlen;
elmbyval = my_extra->typbyval;
elmalign = my_extra->typalign;
elmalignby = typalign_to_alignby(elmalign);
/* compute required space */
if (!isnull)
@ -6204,7 +6214,7 @@ array_fill_internal(ArrayType *dims, ArrayType *lbs,
value = PointerGetDatum(PG_DETOAST_DATUM(value));
nbytes = att_addlength_datum(0, elmlen, value);
nbytes = att_align_nominal(nbytes, elmalign);
nbytes = att_nominal_alignby(nbytes, elmalignby);
Assert(nbytes > 0);
totbytes = nbytes * nitems;
@ -6228,7 +6238,7 @@ array_fill_internal(ArrayType *dims, ArrayType *lbs,
p = ARR_DATA_PTR(result);
for (i = 0; i < nitems; i++)
p += ArrayCastAndSet(value, elmlen, elmbyval, elmalign, p);
p += ArrayCastAndSet(value, elmlen, elmbyval, elmalignby, p);
}
else
{
@ -6259,9 +6269,6 @@ array_unnest(PG_FUNCTION_ARGS)
array_iter iter;
int nextelem;
int numelems;
int16 elmlen;
bool elmbyval;
char elmalign;
} array_unnest_fctx;
FuncCallContext *funcctx;
@ -6272,6 +6279,9 @@ array_unnest(PG_FUNCTION_ARGS)
if (SRF_IS_FIRSTCALL())
{
AnyArrayType *arr;
int16 elmlen;
bool elmbyval;
char elmalign;
/* create a function context for cross-call persistence */
funcctx = SRF_FIRSTCALL_INIT();
@ -6293,23 +6303,24 @@ array_unnest(PG_FUNCTION_ARGS)
/* allocate memory for user context */
fctx = palloc_object(array_unnest_fctx);
/* initialize state */
array_iter_setup(&fctx->iter, arr);
fctx->nextelem = 0;
fctx->numelems = ArrayGetNItems(AARR_NDIM(arr), AARR_DIMS(arr));
/* get element-type data */
if (VARATT_IS_EXPANDED_HEADER(arr))
{
/* we can just grab the type data from expanded array */
fctx->elmlen = arr->xpn.typlen;
fctx->elmbyval = arr->xpn.typbyval;
fctx->elmalign = arr->xpn.typalign;
elmlen = arr->xpn.typlen;
elmbyval = arr->xpn.typbyval;
elmalign = arr->xpn.typalign;
}
else
get_typlenbyvalalign(AARR_ELEMTYPE(arr),
&fctx->elmlen,
&fctx->elmbyval,
&fctx->elmalign);
&elmlen,
&elmbyval,
&elmalign);
/* initialize state */
array_iter_setup(&fctx->iter, arr, elmlen, elmbyval, elmalign);
fctx->nextelem = 0;
fctx->numelems = ArrayGetNItems(AARR_NDIM(arr), AARR_DIMS(arr));
funcctx->user_fctx = fctx;
MemoryContextSwitchTo(oldcontext);
@ -6324,8 +6335,7 @@ array_unnest(PG_FUNCTION_ARGS)
int offset = fctx->nextelem++;
Datum elem;
elem = array_iter_next(&fctx->iter, &fcinfo->isnull, offset,
fctx->elmlen, fctx->elmbyval, fctx->elmalign);
elem = array_iter_next(&fctx->iter, &fcinfo->isnull, offset);
SRF_RETURN_NEXT(funcctx, elem);
}
@ -6401,6 +6411,7 @@ array_replace_internal(ArrayType *array,
int typlen;
bool typbyval;
char typalign;
uint8 typalignby;
char *arraydataptr;
bits8 *bitmap;
int bitmask;
@ -6445,6 +6456,7 @@ array_replace_internal(ArrayType *array,
typlen = typentry->typlen;
typbyval = typentry->typbyval;
typalign = typentry->typalign;
typalignby = typalign_to_alignby(typalign);
/*
* Detoast values if they are toasted. The replacement value must be
@ -6506,7 +6518,7 @@ array_replace_internal(ArrayType *array,
isNull = false;
elt = fetch_att(arraydataptr, typbyval, typlen);
arraydataptr = att_addlength_datum(arraydataptr, typlen, elt);
arraydataptr = (char *) att_align_nominal(arraydataptr, typalign);
arraydataptr = (char *) att_nominal_alignby(arraydataptr, typalignby);
if (search_isnull)
{
@ -6553,7 +6565,7 @@ array_replace_internal(ArrayType *array,
{
/* Update total result size */
nbytes = att_addlength_datum(nbytes, typlen, values[nresult]);
nbytes = att_align_nominal(nbytes, typalign);
nbytes = att_nominal_alignby(nbytes, typalignby);
/* check for overflow of total request */
if (!AllocSizeIsValid(nbytes))
ereport(ERROR,
@ -6860,6 +6872,7 @@ width_bucket_array_variable(Datum operand,
int typlen = typentry->typlen;
bool typbyval = typentry->typbyval;
char typalign = typentry->typalign;
uint8 typalignby = typalign_to_alignby(typalign);
int left;
int right;
@ -6883,7 +6896,7 @@ width_bucket_array_variable(Datum operand,
for (i = left; i < mid; i++)
{
ptr = att_addlength_pointer(ptr, typlen, ptr);
ptr = (char *) att_align_nominal(ptr, typalign);
ptr = (char *) att_nominal_alignby(ptr, typalignby);
}
locfcinfo->args[0].value = operand;
@ -6908,7 +6921,7 @@ width_bucket_array_variable(Datum operand,
* ensures we do only O(N) array indexing work, not O(N^2).
*/
ptr = att_addlength_pointer(ptr, typlen, ptr);
thresholds_data = (char *) att_align_nominal(ptr, typalign);
thresholds_data = (char *) att_nominal_alignby(ptr, typalignby);
}
}

View file

@ -572,21 +572,22 @@ multirange_size_estimate(TypeCacheEntry *rangetyp, int32 range_count,
RangeType **ranges)
{
char elemalign = rangetyp->rngelemtype->typalign;
uint8 elemalignby = typalign_to_alignby(elemalign);
Size size;
int32 i;
/*
* Count space for MultirangeType struct, items and flags.
*/
size = att_align_nominal(sizeof(MultirangeType) +
Max(range_count - 1, 0) * sizeof(uint32) +
range_count * sizeof(uint8), elemalign);
size = att_nominal_alignby(sizeof(MultirangeType) +
Max(range_count - 1, 0) * sizeof(uint32) +
range_count * sizeof(uint8), elemalignby);
/* Count space for range bounds */
for (i = 0; i < range_count; i++)
size += att_align_nominal(VARSIZE(ranges[i]) -
sizeof(RangeType) -
sizeof(char), elemalign);
size += att_nominal_alignby(VARSIZE(ranges[i]) -
sizeof(RangeType) -
sizeof(char), elemalignby);
return size;
}
@ -605,6 +606,7 @@ write_multirange_data(MultirangeType *multirange, TypeCacheEntry *rangetyp,
const char *begin;
char *ptr;
char elemalign = rangetyp->rngelemtype->typalign;
uint8 elemalignby = typalign_to_alignby(elemalign);
items = MultirangeGetItemsPtr(multirange);
flags = MultirangeGetFlagsPtr(multirange);
@ -630,7 +632,7 @@ write_multirange_data(MultirangeType *multirange, TypeCacheEntry *rangetyp,
flags[i] = *((char *) ranges[i] + VARSIZE(ranges[i]) - sizeof(char));
len = VARSIZE(ranges[i]) - sizeof(RangeType) - sizeof(char);
memcpy(ptr, ranges[i] + 1, len);
ptr += att_align_nominal(len, elemalign);
ptr += att_nominal_alignby(len, elemalignby);
}
}

View file

@ -3898,6 +3898,7 @@ array_to_text_internal(FunctionCallInfo fcinfo, ArrayType *v,
int typlen;
bool typbyval;
char typalign;
uint8 typalignby;
StringInfoData buf;
bool printed = false;
char *p;
@ -3947,6 +3948,7 @@ array_to_text_internal(FunctionCallInfo fcinfo, ArrayType *v,
typlen = my_extra->typlen;
typbyval = my_extra->typbyval;
typalign = my_extra->typalign;
typalignby = typalign_to_alignby(typalign);
p = ARR_DATA_PTR(v);
bitmap = ARR_NULLBITMAP(v);
@ -3983,7 +3985,7 @@ array_to_text_internal(FunctionCallInfo fcinfo, ArrayType *v,
printed = true;
p = att_addlength_pointer(p, typlen, p);
p = (char *) att_align_nominal(p, typalign);
p = (char *) att_nominal_alignby(p, typalignby);
}
/* advance bitmap pointer if any */

View file

@ -71,6 +71,43 @@ fetch_att(const void *T, bool attbyval, int attlen)
}
#endif /* FRONTEND */
/*
* typalign_to_alignby: map a TYPALIGN_xxx value to the numeric alignment
* value it represents. (We store TYPALIGN_xxx codes not the real alignment
* values mainly so that initial catalog contents can be machine-independent.)
*/
static inline uint8
typalign_to_alignby(char typalign)
{
uint8 alignby;
switch (typalign)
{
case TYPALIGN_CHAR:
alignby = sizeof(char);
break;
case TYPALIGN_SHORT:
alignby = ALIGNOF_SHORT;
break;
case TYPALIGN_INT:
alignby = ALIGNOF_INT;
break;
case TYPALIGN_DOUBLE:
alignby = ALIGNOF_DOUBLE;
break;
default:
#ifndef FRONTEND
elog(ERROR, "invalid typalign value: %c", typalign);
#else
fprintf(stderr, "invalid typalign value: %c\n", typalign);
exit(1);
#endif
alignby = 0;
break;
}
return alignby;
}
/*
* att_align_datum aligns the given offset as needed for a datum of alignment
* requirement attalign and typlen attlen. attdatum is the Datum variable
@ -139,19 +176,11 @@ fetch_att(const void *T, bool attbyval, int attlen)
* * within arrays and multiranges, we unconditionally align varlenas (XXX this
* should be revisited, probably).
*
* The attalign cases are tested in what is hopefully something like their
* frequency of occurrence.
* In performance-critical loops, avoid using this macro; instead use
* att_nominal_alignby with a pre-computed alignby value.
*/
#define att_align_nominal(cur_offset, attalign) \
( \
((attalign) == TYPALIGN_INT) ? INTALIGN(cur_offset) : \
(((attalign) == TYPALIGN_CHAR) ? (uintptr_t) (cur_offset) : \
(((attalign) == TYPALIGN_DOUBLE) ? DOUBLEALIGN(cur_offset) : \
( \
AssertMacro((attalign) == TYPALIGN_SHORT), \
SHORTALIGN(cur_offset) \
))) \
)
att_nominal_alignby(cur_offset, typalign_to_alignby(attalign))
/*
* Similar to att_align_nominal, but accepts a number of bytes, typically from

View file

@ -22,8 +22,8 @@
* Functions for iterating through elements of a flat or expanded array.
* These require a state struct "array_iter iter".
*
* Use "array_iter_setup(&iter, arrayptr);" to prepare to iterate, and
* "datumvar = array_iter_next(&iter, &isnullvar, index, ...);" to fetch
* Use "array_iter_setup(&iter, arrayptr, ...);" to prepare to iterate,
* and "datumvar = array_iter_next(&iter, &isnullvar, index);" to fetch
* the next element into datumvar/isnullvar.
* "index" must be the zero-origin element number; we make caller provide
* this since caller is generally counting the elements anyway. Despite
@ -42,11 +42,17 @@ typedef struct array_iter
char *dataptr; /* Current spot in the data area */
bits8 *bitmapptr; /* Current byte of the nulls bitmap, or NULL */
int bitmask; /* mask for current bit in nulls bitmap */
/* Fields used in both cases: data about array's element type */
int elmlen;
bool elmbyval;
uint8 elmalignby;
} array_iter;
static inline void
array_iter_setup(array_iter *it, AnyArrayType *a)
array_iter_setup(array_iter *it, AnyArrayType *a,
int elmlen, bool elmbyval, char elmalign)
{
if (VARATT_IS_EXPANDED_HEADER(a))
{
@ -75,11 +81,13 @@ array_iter_setup(array_iter *it, AnyArrayType *a)
it->bitmapptr = ARR_NULLBITMAP((ArrayType *) a);
}
it->bitmask = 1;
it->elmlen = elmlen;
it->elmbyval = elmbyval;
it->elmalignby = typalign_to_alignby(elmalign);
}
static inline Datum
array_iter_next(array_iter *it, bool *isnull, int i,
int elmlen, bool elmbyval, char elmalign)
array_iter_next(array_iter *it, bool *isnull, int i)
{
Datum ret;
@ -98,10 +106,11 @@ array_iter_next(array_iter *it, bool *isnull, int i,
else
{
*isnull = false;
ret = fetch_att(it->dataptr, elmbyval, elmlen);
it->dataptr = att_addlength_pointer(it->dataptr, elmlen,
ret = fetch_att(it->dataptr, it->elmbyval, it->elmlen);
it->dataptr = att_addlength_pointer(it->dataptr, it->elmlen,
it->dataptr);
it->dataptr = (char *) att_align_nominal(it->dataptr, elmalign);
it->dataptr = (char *) att_nominal_alignby(it->dataptr,
it->elmalignby);
}
it->bitmask <<= 1;
if (it->bitmask == 0x100)

View file

@ -735,6 +735,7 @@ PLyList_FromArray_recurse(PLyDatumToOb *elm, int *dims, int ndim, int dim,
char *dataptr = *dataptr_p;
bits8 *bitmap = *bitmap_p;
int bitmask = *bitmask_p;
uint8 typalignby = typalign_to_alignby(elm->typalign);
for (i = 0; i < dims[dim]; i++)
{
@ -751,7 +752,7 @@ PLyList_FromArray_recurse(PLyDatumToOb *elm, int *dims, int ndim, int dim,
itemvalue = fetch_att(dataptr, elm->typbyval, elm->typlen);
PyList_SetItem(list, i, elm->func(elm, itemvalue));
dataptr = att_addlength_pointer(dataptr, elm->typlen, dataptr);
dataptr = (char *) att_align_nominal(dataptr, elm->typalign);
dataptr = (char *) att_nominal_alignby(dataptr, typalignby);
}
/* advance bitmap pointer if any */