BUG #19470: PostgreSQL backend aborts (assert failure) when a prepared statement returns a composite type cast t

Started by PG Bug reporting form5 months ago4 messagesbugs
Beta feature

Hackorum builds and tests every patch posted to the lists, not only commitfest submissions. This is Hackorum's own CI rather than the PostgreSQL project's, and it is still under testing - please report anything that looks wrong.

appliessuccessCI history

You can run a PostgreSQL built from this patch straight from Docker, with no checkout and no build:

docker run --rm -p 5432:5432 ghcr.io/hackorum-dev/postgres-patch:t139534
psql -h localhost -U postgres

Built from patchset v3 (message #3), September 21, 2026 at 12:49 AM.

Every patchset is also pushed to a branch of our PostgreSQL fork, so you can check out the same tree CI built. Without a PostgreSQL checkout:

git clone --branch t139534_3 https://github.com/hackorum-dev/postgres.git

In a checkout you already have, add the fork once:

git remote add hackorum https://github.com/hackorum-dev/postgres.git

then, for this patchset and every later one:

git fetch hackorum t139534_3 && git checkout t139534_3

Patchset v3 (message #3) is on t139534_3

Jump to latest
#1PG Bug reporting form
noreply@postgresql.org

The following bug has been logged on the website:

Bug reference: 19470
Logged by: HaoGang Mao
Email address: haogangmao@gmail.com
PostgreSQL version: 18.3
Operating system: Linux
Description:

Reproduction steps (minimal):
BEGIN;
CREATE TYPE foo AS (a int, b text);
PREPARE p AS SELECT CAST(ROW(1, 'hello') AS foo)::text;
EXECUTE p;
ALTER TYPE foo ALTER ATTRIBUTE a TYPE VARCHAR(100);
EXECUTE p;
COMMIT;

Expected: Error message (type modified while a prepared plan / expression is
active)
Actual: Server connection dropped; backend aborts with SIGABRT due to
assertion failure

Server log (trimmed):
TRAP: failed Assert("false"), File: "heaptuple.c", Line: 1417, PID: <pid>
... heap_deform_tuple()

#2Srinath Reddy Sadipiralla
srinath2133@gmail.com
In reply to: PG Bug reporting form (#1)
Re: BUG #19470: PostgreSQL backend aborts (assert failure) when a prepared statement returns a composite type cast t

Hi,

On Sat, May 2, 2026 at 1:27 AM PG Bug reporting form <noreply@postgresql.org>
wrote:

The following bug has been logged on the website:

Bug reference: 19470
Logged by: HaoGang Mao
Email address: haogangmao@gmail.com
PostgreSQL version: 18.3
Operating system: Linux
Description:

Reproduction steps (minimal):
BEGIN;
CREATE TYPE foo AS (a int, b text);
PREPARE p AS SELECT CAST(ROW(1, 'hello') AS foo)::text;
EXECUTE p;
ALTER TYPE foo ALTER ATTRIBUTE a TYPE VARCHAR(100);
EXECUTE p;
COMMIT;

Expected: Error message (type modified while a prepared plan / expression
is
active)
Actual: Server connection dropped; backend aborts with SIGABRT due to
assertion failure

Server log (trimmed):
TRAP: failed Assert("false"), File: "heaptuple.c", Line: 1417, PID: <pid>
... heap_deform_tuple()

Thanks for reporting and the repro, I was able to reproduce this.
The cause of this crash is a cache invalidation failure. When ALTER TYPE is
executed,
the cached plan for the prepared statement is not properly invalidated. So
the executor uses
a stale memory layout during the second execution. This causes tuple
deformation to see type
confusion, reading a 4-byte INT as a Varlena header. The system interprets
the lowest byte
of the integer as a TOAST pointer, reads the adjacent garbage memory as a
TOAST tag,
and triggers the Assert(false) in VARTAG_SIZE, replacing the assertion with
an elog(ERROR)
would prevent the hard crash, it only masks the main issue. I think the
correct fix is that
ALTER TYPE on a composite attribute correctly triggers a plan
re-validation, thoughts?

--
Thanks,
Srinath Reddy Sadipiralla
EDB: https://www.enterprisedb.com/

#3Euler Taveira
euler@eulerto.com
In reply to: PG Bug reporting form (#1)
Re: BUG #19470: PostgreSQL backend aborts (assert failure) when a prepared statement returns a composite type cast t

On Wed, Apr 29, 2026, at 8:27 AM, PG Bug reporting form wrote:

Reproduction steps (minimal):
BEGIN;
CREATE TYPE foo AS (a int, b text);
PREPARE p AS SELECT CAST(ROW(1, 'hello') AS foo)::text;
EXECUTE p;
ALTER TYPE foo ALTER ATTRIBUTE a TYPE VARCHAR(100);
EXECUTE p;
COMMIT;

Thanks for your report! The attached patch fixes this case. I included a
similar test case but I'm fine if the test is not included with this fix. I
decided to create a separate function instead of adding the new conditions to
record_plan_type_dependency() because it keeps the fix simple and isolated.

The crash is reproduced back to v11 which means this fix should be backpatched
to all supported versions.

--
Euler Taveira
EDB https://www.enterprisedb.com/

Attachments:

t139534_3
v1-0001-Fix-a-crash-with-cached-plans-after-changing-comp.patchtext/x-patch; name="=?UTF-8?Q?v1-0001-Fix-a-crash-with-cached-plans-after-changing-comp.patc?= =?UTF-8?Q?h?="Download+81-2
#4Amit Langote
Langote_Amit_f8@lab.ntt.co.jp
In reply to: Euler Taveira (#3)
Re: BUG #19470: PostgreSQL backend aborts (assert failure) when a prepared statement returns a composite type cast t

Hi,

On Tue, May 5, 2026 at 11:37 AM Euler Taveira <euler@eulerto.com> wrote:

On Wed, Apr 29, 2026, at 8:27 AM, PG Bug reporting form wrote:

Reproduction steps (minimal):
BEGIN;
CREATE TYPE foo AS (a int, b text);
PREPARE p AS SELECT CAST(ROW(1, 'hello') AS foo)::text;
EXECUTE p;
ALTER TYPE foo ALTER ATTRIBUTE a TYPE VARCHAR(100);
EXECUTE p;
COMMIT;

Thanks for your report! The attached patch fixes this case. I included a
similar test case but I'm fine if the test is not included with this fix. I
decided to create a separate function instead of adding the new conditions to
record_plan_type_dependency() because it keeps the fix simple and isolated.

The crash is reproduced back to v11 which means this fix should be backpatched
to all supported versions.

Thanks for working on this.

I think this is on the right track. Using relationOids is the right
mechanism, since the DDL doesn't touch the composite's pg_type row and
a TYPEOID inval item wouldn't work here. Doing it in fix_expr_common
covers extract_query_dependencies too.

What I'm less easy about is whether Const and RowExpr are the only
carriers. As written, the TYPTYPE_COMPOSITE test won't look through an
array over a composite or a domain over one, and nodes like
FieldSelect and ConvertRowtypeExpr also carry composite type OIDs.
Could you check whether any of those reproduce a similar crash? If so
we'd want to look through to the element/base type and/or cover more
node types before this goes in.

Modulo that, I'll prepare to push to all supported branches.

--
Thanks, Amit Langote