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

Started by PG Bug reporting formabout 2 months ago4 messagesbugs
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:

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