in src/backend/utils/adt/agtype_util.c [2124:2253]
static void convert_agtype_object(StringInfo buffer, agtentry *pheader,
agtype_value *val, int level)
{
int base_offset;
int agtentry_offset;
int i;
int totallen;
uint32 header;
int num_pairs = val->val.object.num_pairs;
/* Remember where in the buffer this object starts. */
base_offset = buffer->len;
/* Align to 4-byte boundary (any padding counts as part of my data) */
pad_buffer_to_int(buffer);
/*
* Construct the header agtentry and store it in the beginning of the
* variable-length payload.
*/
header = num_pairs | AGT_FOBJECT;
append_to_buffer(buffer, (char *)&header, sizeof(uint32));
/* Reserve space for the agtentrys of the keys and values. */
agtentry_offset = reserve_from_buffer(buffer,
sizeof(agtentry) * num_pairs * 2);
/*
* Iterate over the keys, then over the values, since that is the ordering
* we want in the on-disk representation.
*/
totallen = 0;
for (i = 0; i < num_pairs; i++)
{
agtype_pair *pair = &val->val.object.pairs[i];
int len;
agtentry meta;
/*
* Convert key, producing an agtentry and appending its variable-length
* data to buffer
*/
convert_agtype_scalar(buffer, &meta, &pair->key);
len = AGTE_OFFLENFLD(meta);
totallen += len;
/*
* Bail out if total variable-length data exceeds what will fit in a
* agtentry length field. We check this in each iteration, not just
* once at the end, to forestall possible integer overflow.
*/
if (totallen > AGTENTRY_OFFLENMASK)
{
ereport(
ERROR,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
errmsg(
"total size of agtype object elements exceeds the maximum of %u bytes",
AGTENTRY_OFFLENMASK)));
}
/*
* Convert each AGT_OFFSET_STRIDE'th length to an offset.
*/
if ((i % AGT_OFFSET_STRIDE) == 0)
meta = (meta & AGTENTRY_TYPEMASK) | totallen | AGTENTRY_HAS_OFF;
copy_to_buffer(buffer, agtentry_offset, (char *)&meta,
sizeof(agtentry));
agtentry_offset += sizeof(agtentry);
}
for (i = 0; i < num_pairs; i++)
{
agtype_pair *pair = &val->val.object.pairs[i];
int len;
agtentry meta;
/*
* Convert value, producing an agtentry and appending its
* variable-length data to buffer
*/
convert_agtype_value(buffer, &meta, &pair->value, level + 1);
len = AGTE_OFFLENFLD(meta);
totallen += len;
/*
* Bail out if total variable-length data exceeds what will fit in a
* agtentry length field. We check this in each iteration, not just
* once at the end, to forestall possible integer overflow.
*/
if (totallen > AGTENTRY_OFFLENMASK)
{
ereport(
ERROR,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
errmsg(
"total size of agtype object elements exceeds the maximum of %u bytes",
AGTENTRY_OFFLENMASK)));
}
/*
* Convert each AGT_OFFSET_STRIDE'th length to an offset.
*/
if (((i + num_pairs) % AGT_OFFSET_STRIDE) == 0)
meta = (meta & AGTENTRY_TYPEMASK) | totallen | AGTENTRY_HAS_OFF;
copy_to_buffer(buffer, agtentry_offset, (char *)&meta,
sizeof(agtentry));
agtentry_offset += sizeof(agtentry);
}
/* Total data size is everything we've appended to buffer */
totallen = buffer->len - base_offset;
/* Check length again, since we didn't include the metadata above */
if (totallen > AGTENTRY_OFFLENMASK)
{
ereport(
ERROR,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
errmsg(
"total size of agtype object elements exceeds the maximum of %u bytes",
AGTENTRY_OFFLENMASK)));
}
/* Initialize the header of this node in the container's agtentry array */
*pheader = AGTENTRY_IS_CONTAINER | totallen;
}