public inbox for [email protected]  
help / color / mirror / Atom feed
From: surya poondla <[email protected]>
To: Andrey Borodin <[email protected]>
Cc: songjinzhou <[email protected]>
Cc: dllggyx <[email protected]>
Cc: pgsql-bugs <[email protected]>
Subject: Re: BUG #19382: Server crash at __nss_database_lookup
Date: Thu, 2 Apr 2026 16:14:00 -0700
Message-ID: <CAOVWO5oRGPd7mA3d85jNYmjLNfeBAca5oDcHTfRFxbAwPLxs5g@mail.gmail.com> (raw)
In-Reply-To: <[email protected]>
References: <[email protected]>
	<CAOVWO5rVBKsjG4YwO_PJQu2OBGp8qUdF1jineYY6Lm3zc6-KWQ@mail.gmail.com>
	<CAOVWO5rbwKgHWLYJMvKuvGxW9eFSk7LADk=ZxDEvwA1uTefvAg@mail.gmail.com>
	<CAOVWO5qN8UXdwMnDa+a7aVq=irGSfm2aeYvEc-uV6j4QHZiyrA@mail.gmail.com>
	<CAOVWO5py4zLPYsnGK1EEzHnC4feTSmbsEZn-BiDgY_=J1P6Wmw@mail.gmail.com>
	<CAOVWO5pbgCVx0zgTr1mxZug2hoGwxZOk+-Owvwg0jaQv9JE3Fw@mail.gmail.com>
	<CAOVWO5r19cctAFKbW24jfMKsD-pkyV21w+z7L3pCPxM1CArtjQ@mail.gmail.com>
	<CAOVWO5oSeBouPv0ueVByh+_6EgRCjWh0spSmnF6Cv-TF1twqKg@mail.gmail.com>
	<[email protected]>
	<CAOVWO5oH37CETZuxxXw3dhCMOHPMA0xFoJBWTpfJ06OV7sGzTQ@mail.gmail.com>
	<[email protected]>

Hi All,

Thanks for the review Andrey.

On Thu, Apr 2, 2026 at 4:18 AM Andrey Borodin <[email protected]> wrote:

> Hi!
>
> Thanks for working on this!
>
> > On 20 Mar 2026, at 23:16, surya poondla <[email protected]> wrote:
> >
> > I'll post an updated patch with this improvement.
>
> After your patch Postgres still crashes on this test:
>
> CREATE TYPE foo AS (a INT, b INT);
> BEGIN;
> DECLARE c CURSOR FOR SELECT (i, power(2, 30))::foo FROM
> generate_series(1,10) i;
> FETCH c;
> ALTER TYPE foo ALTER ATTRIBUTE b TYPE TEXT;
> FETCH c;
> COMMIT;
>
> This test case was proposed in this thread, but I suggest treating this as
> a separate bug needing separate fix.
>

Thank you for reporting this. Yes the cursor case can be treated as a
separate bug.
Though the 2 crash scenarios have the same root cause (record_out()
interpreting old data with new type definition) they require different fix
requirements.
1. PL/pgSQL case (this patch): ExpandedRecords already carry er_tupdesc_id
the version tracking infrastructure exists. The fix detects the mismatch
and converts the data. This is a self-contained bug fix using existing
mechanisms.
2. Cursor case: Flat HeapTuples carry no type version information, they
only have the type OID, which doesn't change after ALTER TYPE. Fixing this
requires adding new infrastructure that PostgreSQL doesn't have today
(e.g., storing tupDesc_identifier in Portal structures, or adding version
fields to HeapTupleHeaders). This is a broader architectural change that
affects core structures like PortalData, pquery.c, and potentially
portalmem.c. We need to see how to add version tracking to composite-type
values. I will work on this fix in parallel.



> In my opinion in both cases (PL/pgSQL + CURSOR) we should error out
> instead of trying to remediate type changes.
>
> I've simplified the fix. Instead of converting the record data, we now
raise a clear error when a composite type is altered mid-transaction after
the record was populated.
This also addresses the performance concern raised earlier since there's no
conversion logic at all now.



Updated patch attached.

Regards,
Surya Poondla


Attachments:

  [application/octet-stream] 0004-Fix-bug-19382-server-crash-when-ALTER-TYPE-is-used-m.patch (4.2K, 3-0004-Fix-bug-19382-server-crash-when-ALTER-TYPE-is-used-m.patch)
  download | inline diff:
From 5c8867d4230beeb1a3d12cf45d699ea9ad027180 Mon Sep 17 00:00:00 2001
From: spoondla <[email protected]>
Date: Fri, 23 Jan 2026 17:28:54 -0800
Subject: [PATCH v4] Fix (bug #19382) server crash when ALTER TYPE is used
 mid-transaction in PL/pgSQL

When ALTER TYPE changes a composite type's column types within a
transaction, PL/pgSQL record variables that were populated before
the ALTER still hold data in the old format. Returning such records
causes a crash because the output functions expect data matching the
new type definition, not the old one.

The crash manifested as a segmentation fault in record_out() when it
attempted to interpret integer data as a text pointer, due to the
mismatch between the stored data and the current type definition.
---
 src/pl/plpgsql/src/pl_exec.c | 69 +++++++++++++++++++++++++++++++++++-
 1 file changed, 68 insertions(+), 1 deletion(-)

diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index 65b0fd0790f..6250c4a748b 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -470,6 +470,7 @@ static char *format_preparedparamsdata(PLpgSQL_execstate *estate,
 static PLpgSQL_variable *make_callstmt_target(PLpgSQL_execstate *estate,
 											  PLpgSQL_expr *expr);
 
+static void check_record_type_not_altered(PLpgSQL_rec *rec);
 
 /* ----------
  * plpgsql_exec_function	Called by the call handler for
@@ -3287,8 +3288,30 @@ exec_stmt_return(PLpgSQL_execstate *estate, PLpgSQL_stmt_return *stmt)
 				}
 				break;
 
-			case PLPGSQL_DTYPE_ROW:
 			case PLPGSQL_DTYPE_REC:
+				{
+					PLpgSQL_rec *rec = (PLpgSQL_rec *) retvar;
+					int32		rettypmod;
+
+					/*
+					 * Check if the record's composite type was altered since
+					 * the record was populated. If so, raise an error to
+					 * prevent crashes when outputting the record.
+					 */
+					if (rec->rectypeid != RECORDOID && rec->erh != NULL &&
+						!ExpandedRecordIsEmpty(rec->erh))
+						check_record_type_not_altered(rec);
+
+					exec_eval_datum(estate,
+									retvar,
+									&estate->rettype,
+									&rettypmod,
+									&estate->retval,
+									&estate->retisnull);
+				}
+				break;
+
+			case PLPGSQL_DTYPE_ROW:
 				{
 					/* exec_eval_datum can handle these cases */
 					int32		rettypmod;
@@ -3434,6 +3457,14 @@ exec_stmt_return_next(PLpgSQL_execstate *estate,
 					TupleDesc	rec_tupdesc;
 					TupleConversionMap *tupmap;
 
+					/*
+					 * Check if the record's composite type was altered since
+					 * the record was populated. If so, raise an error to
+					 * prevent crashes when storing to the tuplestore.
+					 */
+					if (rec->rectypeid != RECORDOID && rec->erh != NULL)
+						check_record_type_not_altered(rec);
+
 					/* If rec is null, try to convert it to a row of nulls */
 					if (rec->erh == NULL)
 						instantiate_empty_record_variable(estate, rec);
@@ -9216,3 +9247,39 @@ format_preparedparamsdata(PLpgSQL_execstate *estate,
 
 	return paramstr.data;
 }
+
+/*
+ * check_record_type_not_altered
+ *
+ * Check if a record's composite type has been altered since the record
+ * was populated. If so, raise an error to prevent crashes that would
+ * occur when outputting data that no longer matches the current type
+ * definition.
+ */
+static void
+check_record_type_not_altered(PLpgSQL_rec *rec)
+{
+	ExpandedRecordHeader *erh = rec->erh;
+	TypeCacheEntry *typentry;
+
+	/* Nothing to do for anonymous RECORD type */
+	if (rec->rectypeid == RECORDOID)
+		return;
+
+	/* Get current type definition from typcache */
+	typentry = lookup_type_cache(rec->rectypeid,
+								 TYPECACHE_TUPDESC |
+								 TYPECACHE_DOMAIN_BASE_INFO);
+	if (typentry->typtype == TYPTYPE_DOMAIN)
+		typentry = lookup_type_cache(typentry->domainBaseType,
+									 TYPECACHE_TUPDESC);
+
+	/* If type has changed since the record was populated, raise an error */
+	if (erh->er_tupdesc_id != typentry->tupDesc_identifier)
+		ereport(ERROR,
+				(errcode(ERRCODE_DATATYPE_MISMATCH),
+				 errmsg("cannot return record variable \"%s\" after its composite type was altered",
+						rec->refname),
+				 errdetail("ALTER TYPE changed the definition of type \"%s\" after the record was populated.",
+						   format_type_be(rec->rectypeid))));
+}
-- 
2.39.5 (Apple Git-154)



reply

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Reply to all the recipients using the --to and --cc options:
  reply via email

  To: [email protected]
  Cc: [email protected], [email protected], [email protected], [email protected], [email protected]
  Subject: Re: BUG #19382: Server crash at __nss_database_lookup
  In-Reply-To: <CAOVWO5oRGPd7mA3d85jNYmjLNfeBAca5oDcHTfRFxbAwPLxs5g@mail.gmail.com>

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

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