BUG #19636: heap_fill_tuple misses short varlena conversion after EOH_flatten_into, causing 3-byte waste per row

From: PG Bug reporting form <noreply(at)postgresql(dot)org>
To: pgsql-bugs(at)lists(dot)postgresql(dot)org
Cc: 17801022106(at)163(dot)com
Subject: BUG #19636: heap_fill_tuple misses short varlena conversion after EOH_flatten_into, causing 3-byte waste per row
Date: 2026-08-21 09:23:51
Message-ID: 19636-c44abe40ca7e2c4d@postgresql.org
Views: Whole Thread | Raw Message | Download mbox | Resend email
Thread:
Lists: pgsql-bugs

The following bug has been logged on the website:

Bug reference: 19636
Logged by: anliuan
Email address: 17801022106(at)163(dot)com
PostgreSQL version: 19beta1
Operating system: centos
Description:

When a PL/pgSQL function/procedure inserts an array value that arrives as an
expanded object (via EOH_flatten_into), the resulting tuple stores the
varlena with a 4-byte header instead of converting it to a
1-byte short varlena header, wasting 3 bytes per row for arrays <= 127
bytes total size.

Plain INSERT correctly uses the short varlena format; only the PL/pgSQL
expanded-object path is affected.

Steps to reproduce:

CREATE TABLE tt2(a text[]);
INSERT INTO tt2 VALUES('{aaaaa}'); -- plain INSERT

CREATE OR REPLACE FUNCTION call_insert_acl(racl text[]) RETURNS int AS $$
BEGIN
INSERT INTO tt2 VALUES(racl);
RETURN 1;
END;
$$ LANGUAGE plpgsql;

SELECT call_insert_acl('{aaaaa}'); -- PL/pgSQL INSERT

SELECT a, pg_column_size(a) FROM tt2;

Expected result: Both rows should have the same pg_column_size (33 bytes —
short varlena with 1-byte header).

Actual result:

┌─────────────────┬────────────────┬───────────────────────────────┐
│ row │ pg_column_size │ header format │
├─────────────────┼────────────────┼───────────────────────────────┤
│ plain INSERT │ 33 │ short varlena (1-byte header) │
├─────────────────┼────────────────┼───────────────────────────────┤
│ PL/pgSQL INSERT │ 36 │ 4-byte header │
└─────────────────┴────────────────┴───────────────────────────────┘
Root cause:

In src/backend/access/common/heaptuple.c, heap_fill_tuple has four varlena
paths. The EOH_flatten_into path (for expanded objects) produces a
4-byte-header varlena and writes it directly, without checking
VARATT_CAN_MAKE_SHORT:

/* Current code — line ~229 */
if (VARATT_IS_EXTERNAL_EXPANDED(val))
{
ExpandedObjectHeader *eoh = DatumGetEOHP(values[i]);
data = (char *) ATT_ALIGN_NOMINAL(data, att[i]->attalign);
data_length = EOH_get_flat_size(eoh);
EOH_flatten_into(eoh, data, data_length);
/* BUG: no VARATT_CAN_MAKE_SHORT check here */
}

The normal 4-byte-header path correctly performs this check:

/* Normal path — line ~240 */
else if (VARLENA_ATT_IS_PACKABLE(att[i]) && VARATT_CAN_MAKE_SHORT(val))
{
data_length = VARATT_CONVERTED_SHORT_SIZE(val);
SET_VARSIZE_SHORT(data, data_length);
}

heap_compute_data_size has the same omission — it uses EOH_get_flat_size()
without considering the short-header size reduction, so the size calculation
and fill logic are consistently wrong (no memory
overrun, just wasted space).

Responses

Browse pgsql-bugs by date

  From Date Subject
Next Message Zsolt Parragi 2026-08-21 09:36:34 Re: MERGE/SPLIT PARTITIONS issues/questions
Previous Message PG Bug reporting form 2026-08-21 09:02:26 BUG #19635: Missing entry in information_schema.sequences when creating a Tale with auto increment