public inbox for [email protected]  
help / color / mirror / Atom feed
BUG #19470: PostgreSQL backend aborts (assert failure) when a prepared statement returns a composite type cast t
2+ messages / 2 participants
[nested] [flat]

* BUG #19470: PostgreSQL backend aborts (assert failure) when a prepared statement returns a composite type cast t
@ 2026-04-29 11:27  PG Bug reporting form <[email protected]>
  0 siblings, 1 reply; 2+ messages in thread

From: PG Bug reporting form @ 2026-04-29 11:27 UTC (permalink / raw)
  To: [email protected]; +Cc: [email protected]

The following bug has been logged on the website:

Bug reference:      19470
Logged by:          HaoGang Mao
Email address:      [email protected]
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()








^ permalink  raw  reply  [nested|flat] 2+ messages in thread

* Re: BUG #19470: PostgreSQL backend aborts (assert failure) when a prepared statement returns a composite type cast t
@ 2026-05-05 02:36  Euler Taveira <[email protected]>
  parent: PG Bug reporting form <[email protected]>
  0 siblings, 0 replies; 2+ messages in thread

From: Euler Taveira @ 2026-05-05 02:36 UTC (permalink / raw)
  To: [email protected]; [email protected]

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:

  [text/x-patch] v1-0001-Fix-a-crash-with-cached-plans-after-changing-comp.patch (5.4K, 2-v1-0001-Fix-a-crash-with-cached-plans-after-changing-comp.patch)
  download | inline diff:
From 96c408c5029ea274a5a9599ffd9a97ef498e994e Mon Sep 17 00:00:00 2001
From: Euler Taveira <[email protected]>
Date: Mon, 4 May 2026 13:13:04 -0300
Subject: [PATCH v1] Fix a crash with cached plans after changing composite
 types

If an existing prepared statement contains a composite type and a
subsequent ALTER TYPE command changes one of the attribute types, the
plan cache was never invalidated, resulting in a crash.

Add the composite type relation of any named composite type that appears
in Const and RowExpr nodes. The ALTER TYPE ... ALTER ATTRIBUTE command
already has the mechanism to send a relcache invalidation. It requires
that the composite type relation is added to relationOids list so the
PlanCacheRelCallback can find and invalidate the affected plans.

Bug: #19470
Reported-by: HaoGang Mao <[email protected]>
Author: Euler Taveira <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/backend/optimizer/plan/setrefs.c    | 39 ++++++++++++++++++++++++-
 src/test/regress/expected/plancache.out | 25 ++++++++++++++++
 src/test/regress/sql/plancache.sql      | 18 ++++++++++++
 3 files changed, 81 insertions(+), 1 deletion(-)

diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c
index ff0e875f2a2..fb73de0bd27 100644
--- a/src/backend/optimizer/plan/setrefs.c
+++ b/src/backend/optimizer/plan/setrefs.c
@@ -29,6 +29,7 @@
 #include "rewrite/rewriteManip.h"
 #include "tcop/utility.h"
 #include "utils/syscache.h"
+#include "utils/typcache.h"
 
 
 typedef enum
@@ -210,7 +211,8 @@ static List *set_returning_clause_references(PlannerInfo *root,
 static List *set_windowagg_runcondition_references(PlannerInfo *root,
 												   List *runcondition,
 												   Plan *plan);
-
+static void record_plan_composite_type_dependency(PlannerInfo *root,
+												  Oid typid);
 static void record_elided_node(PlannerGlobal *glob, int plan_node_id,
 							   NodeTag elided_type, Bitmapset *relids);
 
@@ -2157,6 +2159,13 @@ fix_expr_common(PlannerInfo *root, Node *node)
 			root->glob->relationOids =
 				lappend_oid(root->glob->relationOids,
 							DatumGetObjectId(con->constvalue));
+
+		record_plan_composite_type_dependency(root, con->consttype);
+	}
+	else if (IsA(node, RowExpr))
+	{
+		record_plan_composite_type_dependency(root,
+											  ((RowExpr *) node)->row_typeid);
 	}
 	else if (IsA(node, GroupingFunc))
 	{
@@ -3692,6 +3701,34 @@ record_plan_type_dependency(PlannerInfo *root, Oid typid)
 	}
 }
 
+/*
+ * record_plan_composite_type_dependency
+ *      Mark the current plan as depending on a particular composite type.
+ *
+ * Add composite type relation to the list of the relations the plan depends
+ * on. This ensures that when ALTER TYPE ... ALTER ATTRIBUTE is executed, any
+ * plans that use this composite type will be invalidated.
+ */
+static void
+record_plan_composite_type_dependency(PlannerInfo *root, Oid typid)
+{
+	/*
+	 * As in record_plan_function_dependency, ignore the possibility that
+	 * someone would change a built-in composite type. Anonymous record types
+	 * are not considered.
+	 */
+	if (typid >= (Oid) FirstUnpinnedObjectId)
+	{
+		TypeCacheEntry *typentry;
+
+		typentry = lookup_type_cache(typid, 0);
+		if (typentry->typtype == TYPTYPE_COMPOSITE &&
+			OidIsValid(typentry->typrelid))
+			root->glob->relationOids =
+				lappend_oid(root->glob->relationOids, typentry->typrelid);
+	}
+}
+
 /*
  * extract_query_dependencies
  *		Given a rewritten, but not yet planned, query or queries
diff --git a/src/test/regress/expected/plancache.out b/src/test/regress/expected/plancache.out
index d58534ca1cd..766c430c862 100644
--- a/src/test/regress/expected/plancache.out
+++ b/src/test/regress/expected/plancache.out
@@ -402,3 +402,28 @@ select name, generic_plans, custom_plans from pg_prepared_statements
 (1 row)
 
 drop table test_mode;
+-- bug #19470: ALTER TYPE ... ALTER ATTRIBUTE should invalidate plans.
+-- Test compatible and incompatible attribute type changes.
+CREATE TYPE bug_19470 AS (a int, b text);
+PREPARE stmt_19470 AS SELECT CAST(ROW(1, 'hello') AS bug_19470)::text;
+EXECUTE stmt_19470;
+    row    
+-----------
+ (1,hello)
+(1 row)
+
+-- should be ok
+ALTER TYPE bug_19470 ALTER ATTRIBUTE a TYPE varchar(100);
+EXECUTE stmt_19470;
+    row    
+-----------
+ (1,hello)
+(1 row)
+
+-- should fail
+ALTER TYPE bug_19470 ALTER ATTRIBUTE a TYPE timestamp without time zone;
+EXECUTE stmt_19470;
+ERROR:  cannot cast type record to bug_19470
+DETAIL:  Cannot cast type integer to timestamp without time zone in column 1.
+DEALLOCATE stmt_19470;
+DROP TYPE bug_19470;
diff --git a/src/test/regress/sql/plancache.sql b/src/test/regress/sql/plancache.sql
index aed388d03a1..842a2137a37 100644
--- a/src/test/regress/sql/plancache.sql
+++ b/src/test/regress/sql/plancache.sql
@@ -228,3 +228,21 @@ select name, generic_plans, custom_plans from pg_prepared_statements
   where  name = 'test_mode_pp';
 
 drop table test_mode;
+
+-- bug #19470: ALTER TYPE ... ALTER ATTRIBUTE should invalidate plans.
+-- Test compatible and incompatible attribute type changes.
+CREATE TYPE bug_19470 AS (a int, b text);
+
+PREPARE stmt_19470 AS SELECT CAST(ROW(1, 'hello') AS bug_19470)::text;
+EXECUTE stmt_19470;
+
+-- should be ok
+ALTER TYPE bug_19470 ALTER ATTRIBUTE a TYPE varchar(100);
+EXECUTE stmt_19470;
+
+-- should fail
+ALTER TYPE bug_19470 ALTER ATTRIBUTE a TYPE timestamp without time zone;
+EXECUTE stmt_19470;
+
+DEALLOCATE stmt_19470;
+DROP TYPE bug_19470;
-- 
2.39.5



^ permalink  raw  reply  [nested|flat] 2+ messages in thread


end of thread, other threads:[~2026-05-05 02:36 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2026-04-29 11:27 BUG #19470: PostgreSQL backend aborts (assert failure) when a prepared statement returns a composite type cast t PG Bug reporting form <[email protected]>
2026-05-05 02:36 ` Euler Taveira <[email protected]>

This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox