BUG #19382: Server crash at __nss_database_lookup

Started by PG Bug reporting form3 months ago16 messagesbugs
Jump to latest
#1PG Bug reporting form
noreply@postgresql.org

The following bug has been logged on the website:

Bug reference: 19382
Logged by: Yuxiao Guo
Email address: dllggyx@outlook.com
PostgreSQL version: 17.7
Operating system: Ubuntu 20.04 x86-64, docker image postgres:17.7
Description:

Hi, I found a crash in PostgreSQL. Here are the details:

PoC:
DROP FUNCTION IF EXISTS bar();
DROP TYPE IF EXISTS foo CASCADE;
CREATE TYPE foo AS (a INT, b INT);
CREATE FUNCTION bar() RETURNS RECORD AS $$
DECLARE
r foo := ROW(123, power(2, 30));
BEGIN
ALTER TYPE foo ALTER ATTRIBUTE b TYPE TEXT;
RETURN r;
END;
$$ LANGUAGE plpgsql;
SELECT bar();

DROP TYPE IF EXISTS foo CASCADE;
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;

Stacktrace:
#0 0x7182a7813b38 (__nss_database_lookup+0x284b8)
#1 0x91c9e5 (textout+0x85)
#2 0x94cbd3 (OutputFunctionCall+0x33)
#3 0x8db524 (record_out+0x244)
#4 0x94cbd3 (OutputFunctionCall+0x33)
#5 0x49fafc (printtup+0x2bc)
#6 0x7fc888 (RunFromStore+0xa8)
#7 0x7fbeb5 (PortalRunSelect+0x75)
#8 0x7fbbbe (PortalRun+0x16e)
#9 0x7fadd5 (exec_simple_query+0x585)
#10 0x7f8929 (PostgresMain+0x949)
#11 0x7f45bf (BackendMain+0x3f)
#12 0x75df24 (postmaster_child_launch+0x94)
#13 0x762101 (ServerLoop+0x1d61)
#14 0x75faab (PostmasterMain+0xd8b)
#15 0x6a3349 (main+0x2f9)
#16 0x7182a76ac083 (__libc_start_main+0xf3)
#17 0x49006e (_start+0x2e)

#2Kirill Reshke
reshkekirill@gmail.com
In reply to: PG Bug reporting form (#1)
Re: BUG #19382: Server crash at __nss_database_lookup

On Tue, 20 Jan 2026 at 13:58, PG Bug reporting form
<noreply@postgresql.org> wrote:

The following bug has been logged on the website:

Bug reference: 19382
Logged by: Yuxiao Guo
Email address: dllggyx@outlook.com
PostgreSQL version: 17.7
Operating system: Ubuntu 20.04 x86-64, docker image postgres:17.7
Description:

Hi, I found a crash in PostgreSQL. Here are the details:

PoC:
DROP FUNCTION IF EXISTS bar();
DROP TYPE IF EXISTS foo CASCADE;
CREATE TYPE foo AS (a INT, b INT);
CREATE FUNCTION bar() RETURNS RECORD AS $$
DECLARE
r foo := ROW(123, power(2, 30));
BEGIN
ALTER TYPE foo ALTER ATTRIBUTE b TYPE TEXT;
RETURN r;
END;
$$ LANGUAGE plpgsql;
SELECT bar();

DROP TYPE IF EXISTS foo CASCADE;
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;

Stacktrace:
#0 0x7182a7813b38 (__nss_database_lookup+0x284b8)
#1 0x91c9e5 (textout+0x85)
#2 0x94cbd3 (OutputFunctionCall+0x33)
#3 0x8db524 (record_out+0x244)
#4 0x94cbd3 (OutputFunctionCall+0x33)
#5 0x49fafc (printtup+0x2bc)
#6 0x7fc888 (RunFromStore+0xa8)
#7 0x7fbeb5 (PortalRunSelect+0x75)
#8 0x7fbbbe (PortalRun+0x16e)
#9 0x7fadd5 (exec_simple_query+0x585)
#10 0x7f8929 (PostgresMain+0x949)
#11 0x7f45bf (BackendMain+0x3f)
#12 0x75df24 (postmaster_child_launch+0x94)
#13 0x762101 (ServerLoop+0x1d61)
#14 0x75faab (PostmasterMain+0xd8b)
#15 0x6a3349 (main+0x2f9)
#16 0x7182a76ac083 (__libc_start_main+0xf3)
#17 0x49006e (_start+0x2e)

reproduced here on HEAD

--
Best regards,
Kirill Reshke

#3Kirill Reshke
reshkekirill@gmail.com
In reply to: Kirill Reshke (#2)
Re: BUG #19382: Server crash at __nss_database_lookup

On Tue, 20 Jan 2026 at 14:15, Kirill Reshke <reshkekirill@gmail.com> wrote:

On Tue, 20 Jan 2026 at 13:58, PG Bug reporting form
<noreply@postgresql.org> wrote:

The following bug has been logged on the website:

Bug reference: 19382
Logged by: Yuxiao Guo
Email address: dllggyx@outlook.com
PostgreSQL version: 17.7
Operating system: Ubuntu 20.04 x86-64, docker image postgres:17.7
Description:

Hi, I found a crash in PostgreSQL. Here are the details:

PoC:
DROP FUNCTION IF EXISTS bar();
DROP TYPE IF EXISTS foo CASCADE;
CREATE TYPE foo AS (a INT, b INT);
CREATE FUNCTION bar() RETURNS RECORD AS $$
DECLARE
r foo := ROW(123, power(2, 30));
BEGIN
ALTER TYPE foo ALTER ATTRIBUTE b TYPE TEXT;
RETURN r;
END;
$$ LANGUAGE plpgsql;
SELECT bar();

DROP TYPE IF EXISTS foo CASCADE;
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;

Stacktrace:
#0 0x7182a7813b38 (__nss_database_lookup+0x284b8)
#1 0x91c9e5 (textout+0x85)
#2 0x94cbd3 (OutputFunctionCall+0x33)
#3 0x8db524 (record_out+0x244)
#4 0x94cbd3 (OutputFunctionCall+0x33)
#5 0x49fafc (printtup+0x2bc)
#6 0x7fc888 (RunFromStore+0xa8)
#7 0x7fbeb5 (PortalRunSelect+0x75)
#8 0x7fbbbe (PortalRun+0x16e)
#9 0x7fadd5 (exec_simple_query+0x585)
#10 0x7f8929 (PostgresMain+0x949)
#11 0x7f45bf (BackendMain+0x3f)
#12 0x75df24 (postmaster_child_launch+0x94)
#13 0x762101 (ServerLoop+0x1d61)
#14 0x75faab (PostmasterMain+0xd8b)
#15 0x6a3349 (main+0x2f9)
#16 0x7182a76ac083 (__libc_start_main+0xf3)
#17 0x49006e (_start+0x2e)

reproduced here on HEAD

In fact, this fails as fast as 'SELECT bar()' for me. Also, it fails
for REL_14_STABLE, REL_16_STABLE, so, problem is for all supported
versions

--
Best regards,
Kirill Reshke

#4Kirill Reshke
reshkekirill@gmail.com
In reply to: Kirill Reshke (#3)
Re: BUG #19382: Server crash at __nss_database_lookup

In fact, this fails as fast as 'SELECT bar()' for me. Also, it fails
for REL_14_STABLE, REL_16_STABLE, so, problem is for all supported
versions

Below fails in same way:

"
CREATE TABLE foo (a INT, b INT);
CREATE FUNCTION bar() RETURNS RECORD AS $$
DECLARE
r foo := ROW(1, 1);
BEGIN
ALTER table foo alter column b type text;
RETURN r;
END;
$$ LANGUAGE plpgsql;
SELECT bar();
"

My current idea is that we should somehow reject the ALTER type if it
is used to declare part of a function. Maybe exec_assign_expr should
report all types that are used to evaluate expressions in the declare
part?

--
Best regards,
Kirill Reshke

#5surya poondla
suryapoondla4@gmail.com
In reply to: PG Bug reporting form (#1)
Re: BUG #19382: Server crash at __nss_database_lookup

Hi Yuxiao, Kirill,

Thank you for the test cases.

I can reproduce this issue on PostgreSQL 17.6. I debugged it with lldb and
found the root cause.

When a composite type is altered mid-transaction while a PL/pgSQL record
variable holds data of that type, the server crashes because it interprets
old data using the new type definition without performing type conversion.

The server crashes with this stack trace:

* thread #1, queue = 'main-thread', stop reason = EXC_BAD_ACCESS (code=1,
address=0x117e00000)

frame #0: 0x0000000183c95320 libsystem_platform.dylib`_platform_memmove
+ 96

libsystem_platform.dylib`_platform_memmove:

-> 0x183c95320 <+96>: ldnp q0, q1, [x1]

0x183c95324 <+100>: add x1, x1, #0x20

0x183c95328 <+104>: subs x2, x2, #0x20

0x183c9532c <+108>: b.hi 0x183c95318 ; <+88>

Target 0: (postgres) stopped.

(lldb) bt

* thread #1, queue = 'main-thread', stop reason = EXC_BAD_ACCESS (code=1,
address=0x117e00000)

* frame #0: 0x0000000183c95320 libsystem_platform.dylib`_platform_memmove
+ 96

frame #1: 0x00000001030ef368
postgres`text_to_cstring(t=0x0000000117017b1c) at varlena.c:225:2

frame #2: 0x00000001030f0e58
postgres`textout(fcinfo=0x000000016d5f1b98) at varlena.c:594:2

frame #3: 0x000000010314ed14
postgres`FunctionCall1Coll(flinfo=0x0000000121808cd8, collation=0,
arg1=4680940316) at fmgr.c:1139:11

frame #4: 0x0000000103150880
postgres`OutputFunctionCall(flinfo=0x0000000121808cd8, val=4680940316) at
fmgr.c:1685:25

frame #5: 0x0000000103075c8c
postgres`record_out(fcinfo=0x000000016d5f1d58) at rowtypes.c:435:11

frame #6: 0x000000010314ed14
postgres`FunctionCall1Coll(flinfo=0x0000000121808a28, collation=0,
arg1=4940960546) at fmgr.c:1139:11

frame #7: 0x0000000103150880
postgres`OutputFunctionCall(flinfo=0x0000000121808a28, val=4940960546) at
fmgr.c:1685:25

frame #8: 0x000000010282fa30 postgres`printtup(slot=0x00000001218087a8,
self=0x00000001170102d8) at printtup.c:360:16

frame #9: 0x0000000102b8fdac
postgres`ExecutePlan(queryDesc=0x0000000137010300, operation=CMD_SELECT,
sendTuples=true, numberTuples=0, direction=ForwardScanDirection,
dest=0x00000001170102d8) at execMain.c:1679:9

frame #10: 0x0000000102b8fb98
postgres`standard_ExecutorRun(queryDesc=0x0000000137010300,
direction=ForwardScanDirection, count=0, execute_once=false) at
execMain.c:360:3

frame #11: 0x0000000102b8f988
postgres`ExecutorRun(queryDesc=0x0000000137010300,
direction=ForwardScanDirection, count=0, execute_once=false) at
execMain.c:306:3

frame #12: 0x0000000102ee2bd4
postgres`PortalRunSelect(portal=0x000000012782c500, forward=true, count=0,
dest=0x00000001170102d8) at pquery.c:922:4

frame #13: 0x0000000102ee2568
postgres`PortalRun(portal=0x000000012782c500, count=9223372036854775807,
isTopLevel=true, run_once=true, dest=0x00000001170102d8,
altdest=0x00000001170102d8, qc=0x000000016d5f21b8) at pquery.c:766:18

frame #14: 0x0000000102edce9c
postgres`exec_simple_query(query_string="SELECT bar();") at
postgres.c:1278:10

frame #15: 0x0000000102edbf6c postgres`PostgresMain(dbname="postgres",
username="surya") at postgres.c:4767:7

frame #16: 0x0000000102ed3594 postgres`BackendMain(startup_data="",
startup_data_len=4) at backend_startup.c:106:2

frame #17: 0x0000000102daf8f8
postgres`postmaster_child_launch(child_type=B_BACKEND, startup_data="",
startup_data_len=4, client_sock=0x000000016d5f25b8) at
launch_backend.c:277:3

frame #18: 0x0000000102db7708
postgres`BackendStartup(client_sock=0x000000016d5f25b8) at
postmaster.c:3624:8

frame #19: 0x0000000102db4438 postgres`ServerLoop at postmaster.c:1678:6

frame #20: 0x0000000102db3324 postgres`PostmasterMain(argc=3,
argv=0x000060000321d420) at postmaster.c:1376:11

frame #21: 0x0000000102c369c0 postgres`main(argc=3,
argv=0x000060000321d420) at main.c:199:3

frame #22: 0x00000001838bab98 dyld`start + 6076

(lldb)

The crash happens because textout() is called on integer data, and it
interprets 1073741824 (2^30) as a memory pointer.

I set breakpoints at two critical points to trace the issue:

Breakpoint 1: ExpandedRecordGetDatum (when PL/pgSQL returns the record)

At this point, the record still has complete version information:

(lldb) p erh->er_tupdesc_id

(uint64) 2 // Record was created with version 2

(lldb) p assign_record_type_identifier(erh->er_typeid, erh->er_typmod)

(uint64) 4 // Current type is now version 4

(lldb) p erh->er_tupdesc->attrs[1].atttypid

(Oid) 23 // Field b was INT4 when record was created

(lldb) p ((TypeCacheEntry*)lookup_type_cache(erh->er_typeid,
0x00100))->tupDesc->attrs[1].atttypid

(Oid) 25 // Field b is now TEXT in current definition

Version mismatch detected (2 != 4). The record has integer data but the
type definition changed to TEXT.

Breakpoint 2: record_out (when converting record to text for output)

After ExpandedRecordGetDatum flattens the record to HeapTupleHeader, the
version information is lost:

(lldb) p tupType

(Oid) 32770 //Only type OID preserved

(lldb) p tupTypmod

(int32) -1 //Only typmod preserved

(lldb) p tupdesc->attrs[1].atttypid

(Oid) 25 // Uses current definition: TEXT

When ExpandedRecordHeader is flattened to HeapTupleHeader, HeapTupleHeader
only stores type OID and typmod but not the version identifier.

This returns the current type definition (version 4, field b = TEXT), but
the actual data is still from version 2 (field b = INT, value = 1073741824).

The crash happens at rowtypes.c, when record_out() calls textout() on field
b. Since textout() expects a text pointer but receives an integer, it tries
to dereference 0x40000000 (1073741824 (2^30)), causing a segfault
that leads to the crash.

I believe the fix should be in pl_exec.c before the record is returned. At
the point where we still have access to erh->er_tupdesc_id, and we can
compare erh->er_tupdesc_id with current tupDesc_identifier, if they differ,
the type was altered. For each field with changed type, apply conversion
using exec_cast_value().

If conversion fails or no cast exists, raise a proper error, if not return
the converted record with updated version

This prevents crashes by either converting the data (INT to TEXT which
should work) or raising a clean error message instead of a segfault.

I am working on a patch for this.

Kindly let me know your thoughts.

#6surya poondla
suryapoondla4@gmail.com
In reply to: surya poondla (#5)
Re: BUG #19382: Server crash at __nss_database_lookup

Hi All,

I am working on a patch for this.

I built a patch on 17.6 postgres version.

The overall issue is:
When ALTER TYPE modifies column types, the type cache updates its
tupDesc_identifier.
However, existing ExpandedRecordHeader instances still reference the old
tupdesc.
When the record is returned and flattened, the output functions expect data
to match the new type definition but receive data in the old format,
causing type confusion (e.g., interpreting an integer as a text pointer).
This was causing a segfault and crashing the server.

In my solution (attached patch), I added convert_record_for_altered_type()
function which detects type changes by comparing er_tupdesc_id against the
current tupDesc_identifier.
When a mismatch is found, it tries to convert each field value to match the
new type definition, if this fails we error out.
convert_record_for_altered_type() function is called in exec_stmt_return()
and exec_stmt_return_next() before returning records.

I tested my patch and see the below output
postgres=# DROP FUNCTION IF EXISTS bar();
DROP FUNCTION
postgres=# DROP TYPE IF EXISTS foo CASCADE;
DROP TYPE
postgres=# DROP FUNCTION IF EXISTS bar1();
DROP FUNCTION
postgres=# DROP TYPE IF EXISTS foo1 CASCADE;
DROP TYPE
postgres=# CREATE TYPE foo AS (a INT, b INT);
CREATE TYPE
postgres=# CREATE FUNCTION bar() RETURNS RECORD AS $$
postgres$# DECLARE
postgres$# r foo := ROW(123, power(2, 30));
postgres$# BEGIN
postgres$# ALTER TYPE foo ALTER ATTRIBUTE b TYPE TEXT;
postgres$# RETURN r;
postgres$# END;
postgres$# $$ LANGUAGE plpgsql;
CREATE FUNCTION
postgres=#
postgres=# SELECT bar();
bar
------------------
(123,1073741824)
(1 row)

postgres=# CREATE TYPE foo1 AS (a INT, b INT);
CREATE TYPE
postgres=#
postgres=# CREATE FUNCTION bar1(OUT r1 foo1) AS $$
postgres$# BEGIN
postgres$# r1 := ROW(1, 2);
postgres$# ALTER TYPE foo1 ALTER ATTRIBUTE b TYPE TEXT;
postgres$# RETURN;
postgres$# END;
postgres$# $$ LANGUAGE plpgsql;
CREATE FUNCTION
postgres=#
postgres=# SELECT bar1();
bar1
-------
(1,2)
(1 row)

postgres=# CREATE TYPE foo2 AS (a INT, b TEXT);
CREATE TYPE
postgres=# CREATE FUNCTION bar2() RETURNS foo2 AS $$
postgres$# DECLARE
postgres$# r foo2 := ROW(1, 'hello');
postgres$# BEGIN
postgres$# ALTER TYPE foo2 ALTER ATTRIBUTE b TYPE INT; -- TEXT → INT
postgres$# RETURN r; -- Should get clean error (not crash)
postgres$# END;
postgres$# $$ LANGUAGE plpgsql;
CREATE FUNCTION
postgres=#
postgres=# SELECT bar2();
ERROR: invalid input syntax for type integer: "hello"
CONTEXT: PL/pgSQL function bar2() line 6 at RETURN
postgres=#

Attachments:

0001-Fix-bug-19382-server-crash-when-ALTER-TYPE-is-used-m.patchapplication/octet-stream; name=0001-Fix-bug-19382-server-crash-when-ALTER-TYPE-is-used-m.patchDownload+161-1
#7surya poondla
suryapoondla4@gmail.com
In reply to: surya poondla (#6)
Re: BUG #19382: Server crash at __nss_database_lookup

Hi All,

I also tested the patch on postgres 19, it shows the same working behavior
as postgres 17.

#8surya poondla
suryapoondla4@gmail.com
In reply to: surya poondla (#7)
Re: BUG #19382: Server crash at __nss_database_lookup

Hi All,

I created a commitfest entry here at:
https://commitfest.postgresql.org/patch/6449/

Show quoted text
#9surya poondla
suryapoondla4@gmail.com
In reply to: surya poondla (#8)
Re: BUG #19382: Server crash at __nss_database_lookup

Hi All,

CFBot on the commitfest asked me to rebase my code, and during the rebase
(rebase to 17 Stable version) I realized my initial patch v1 might have
a memory management issue.
When conversion of type was needed, the old record object was never
properly freed, and the new record was being created in the wrong memory
context. v2 fixes this by passing the record variable directly to
convert_record_for_altered_type() and using assign_record_var() internally
to replace a record variable and this way correctly frees the old value,
transfers the new one into the right memory context.

Testing:
1) All 224 core regression tests pass
2) All 13 PL/pgSQL regression tests pass
3) All original test cases from the bug report produce correct results
(same results as my v1 patch) (adding them here again for quick reference)
and we no longer see the crash of the database.

psql (17.9)

Type "help" for help.

postgres=# DROP FUNCTION IF EXISTS bar();

DROP FUNCTION

postgres=# DROP TYPE IF EXISTS foo CASCADE;

DROP TYPE

postgres=# CREATE TYPE foo AS (a INT, b INT);

CREATE TYPE

postgres=# CREATE FUNCTION bar() RETURNS RECORD AS $$

postgres$# DECLARE

postgres$# r foo := ROW(123, power(2, 30));

postgres$# BEGIN

postgres$# ALTER TYPE foo ALTER ATTRIBUTE b TYPE TEXT;

postgres$# RETURN r;

postgres$# END;

postgres$# $$ LANGUAGE plpgsql;

CREATE FUNCTION

postgres=# SELECT bar();

bar

------------------

(123,1073741824)

(1 row)

postgres=# DROP FUNCTION IF EXISTS bar1();

DROP FUNCTION

postgres=# DROP TYPE IF EXISTS foo1 CASCADE;

DROP TYPE

postgres=# CREATE TYPE foo1 AS (a INT, b INT);

CREATE TYPE

postgres=# CREATE FUNCTION bar1(OUT r1 foo1) AS $$

postgres$# BEGIN

postgres$# r1 := ROW(1, 2);

postgres$# ALTER TYPE foo1 ALTER ATTRIBUTE b TYPE TEXT;

postgres$# RETURN;

postgres$# END;

postgres$# $$ LANGUAGE plpgsql;

CREATE FUNCTION

postgres=# SELECT bar1();

bar1

-------

(1,2)

(1 row)

postgres=# DROP TYPE IF EXISTS foo2 CASCADE;

NOTICE: drop cascades to function bar2()

DROP TYPE

postgres=# CREATE TYPE foo2 AS (a INT, b TEXT);

CREATE TYPE

postgres=# CREATE FUNCTION bar2() RETURNS foo2 AS $$

postgres$# DECLARE

postgres$# r foo2 := ROW(1, 'hello');

postgres$# BEGIN

postgres$# ALTER TYPE foo2 ALTER ATTRIBUTE b TYPE INT;

postgres$# RETURN r;

postgres$# END;

postgres$# $$ LANGUAGE plpgsql;

CREATE FUNCTION

postgres=# SELECT bar2();

ERROR: invalid input syntax for type integer: "hello"

CONTEXT: PL/pgSQL function bar2() line 6 at RETURN

postgres=# quit

Regards,
Surya Poondla

Show quoted text

Attachments:

0002-Fix-bug-19382-server-crash-when-ALTER-TYPE-is-used-m.patchapplication/octet-stream; name=0002-Fix-bug-19382-server-crash-when-ALTER-TYPE-is-used-m.patchDownload+164-2
#10surya poondla
suryapoondla4@gmail.com
In reply to: surya poondla (#9)
Re: BUG #19382: Server crash at __nss_database_lookup

Hi All,

Rebased on the latest 17 code and slightly refactored the code.

Regards,
Surya Poondla

Attachments:

0003-Fix-bug-19382-server-crash-when-ALTER-TYPE-is-used-m.patchapplication/octet-stream; name=0003-Fix-bug-19382-server-crash-when-ALTER-TYPE-is-used-m.patchDownload+172-2
#11surya poondla
suryapoondla4@gmail.com
In reply to: surya poondla (#10)
Re: BUG #19382: Server crash at __nss_database_lookup

Hi All,

I was able to reproduce the crash on laster master (19), the above patch
applies cleanly on postgres 19 and doesn't crash the server.

psql (19devel)
Type "help" for help.

postgres=# DROP FUNCTION IF EXISTS bar();
NOTICE: function bar() does not exist, skipping
DROP FUNCTION
postgres=# DROP TYPE IF EXISTS foo CASCADE;
NOTICE: type "foo" does not exist, skipping
DROP TYPE
postgres=# CREATE TYPE foo AS (a INT, b INT);
CREATE TYPE
postgres=# CREATE FUNCTION bar() RETURNS RECORD AS $$
postgres$# DECLARE
postgres$# r foo := ROW(123, power(2, 30));
postgres$# BEGIN
postgres$# ALTER TYPE foo ALTER ATTRIBUTE b TYPE TEXT;
postgres$# RETURN r;
postgres$# END;
postgres$# $$ LANGUAGE plpgsql;
CREATE FUNCTION
postgres=# SELECT bar();
bar
------------------
(123,1073741824)
(1 row)

postgres=# DROP FUNCTION IF EXISTS bar1();
NOTICE: function bar1() does not exist, skipping
DROP FUNCTION
postgres=# DROP TYPE IF EXISTS foo1 CASCADE;
NOTICE: type "foo1" does not exist, skipping
DROP TYPE
postgres=# CREATE TYPE foo1 AS (a INT, b INT);
CREATE TYPE
postgres=# CREATE FUNCTION bar1(OUT r1 foo1) AS $$
postgres$# BEGIN
postgres$# r1 := ROW(1, 2);
postgres$# ALTER TYPE foo1 ALTER ATTRIBUTE b TYPE TEXT;
postgres$# RETURN;
postgres$# END;
postgres$# $$ LANGUAGE plpgsql;
CREATE FUNCTION
postgres=# SELECT bar1();
bar1
-------
(1,2)
(1 row)

postgres=# DROP FUNCTION IF EXISTS bar2();
NOTICE: function bar2() does not exist, skipping
DROP FUNCTION
postgres=# DROP TYPE IF EXISTS foo2 CASCADE;
NOTICE: type "foo2" does not exist, skipping
DROP TYPE
postgres=# CREATE TYPE foo2 AS (a INT, b TEXT);
CREATE TYPE
postgres=# CREATE FUNCTION bar2() RETURNS foo2 AS $$
postgres$# DECLARE
postgres$# r foo2 := ROW(1, 'hello');
postgres$# BEGIN
postgres$# ALTER TYPE foo2 ALTER ATTRIBUTE b TYPE INT;
postgres$# RETURN r;
postgres$# END;
postgres$# $$ LANGUAGE plpgsql;
CREATE FUNCTION
postgres=# SELECT bar2();
ERROR: invalid input syntax for type integer: "hello"
CONTEXT: PL/pgSQL function bar2() line 6 at RETURN
postgres=# DROP FUNCTION bar();
DROP FUNCTION
postgres=# DROP FUNCTION bar1();
DROP FUNCTION
postgres=# DROP FUNCTION bar2();
DROP FUNCTION
postgres=# DROP TYPE IF EXISTS foo CASCADE;
DROP TYPE
postgres=# DROP TYPE IF EXISTS foo1 CASCADE;
DROP TYPE
postgres=# DROP TYPE IF EXISTS foo2 CASCADE;
DROP TYPE
postgres=# quit

Regards,
Surya Poondla

Attachments:

0003-Fix-bug-19382-server-crash-when-ALTER-TYPE-is-used-m_PG19.patchapplication/octet-stream; name=0003-Fix-bug-19382-server-crash-when-ALTER-TYPE-is-used-m_PG19.patchDownload+172-2
#12songjinzhou
tsinghualucky912@foxmail.com
In reply to: surya poondla (#11)
Re: BUG #19382: Server crash at __nss_database_lookup

Hi Surya Poondla:

After applying the patch on the master branch, I debugged it and the type mismatch issue is indeed not as bad as before. I haven't looked at this patch much, but I'm a little worried about performance issues here.

I have one more question: Can we iterate through the code first to get the value of `need_conversion`, and then allocate memory and perform subsequent operations only if necessary? Of course, this is just my opinion. Thank you.

Regards,
songjinzhou

songjinzhou
tsinghualucky912@foxmail.com

原始邮件

发件人:surya poondla <suryapoondla4@gmail.com&gt;
发件时间:2026年3月19日 13:00
收件人:dllggyx <dllggyx@outlook.com&gt;, pgsql-bugs <pgsql-bugs@lists.postgresql.org&gt;
主题:Re: BUG #19382: Server crash at __nss_database_lookup

Hi All,

I was able to reproduce the crash on laster master (19), the above patch applies cleanly on postgres 19 and doesn't crash the server.

psql (19devel)
Type "help" for help.

postgres=# DROP FUNCTION IF EXISTS bar();
NOTICE: &nbsp;function bar() does not exist, skipping
DROP FUNCTION
postgres=# &nbsp; DROP TYPE IF EXISTS foo CASCADE;
NOTICE: &nbsp;type "foo" does not exist, skipping
DROP TYPE
postgres=# &nbsp; CREATE TYPE foo AS (a INT, b INT);
CREATE TYPE
postgres=# &nbsp; CREATE FUNCTION bar() RETURNS RECORD AS $$
postgres$# &nbsp; DECLARE
postgres$# &nbsp; &nbsp; &nbsp; r foo := ROW(123, power(2, 30));
postgres$# &nbsp; BEGIN
postgres$# &nbsp; &nbsp; &nbsp; ALTER TYPE foo ALTER ATTRIBUTE b TYPE TEXT;
postgres$# &nbsp; &nbsp; &nbsp; RETURN r;
postgres$# &nbsp; END;
postgres$# &nbsp; $$ LANGUAGE plpgsql;
CREATE FUNCTION
postgres=# SELECT bar();
&nbsp; &nbsp; &nbsp; &nbsp;bar
------------------
&nbsp;(123,1073741824)
(1 row)

postgres=# DROP FUNCTION IF EXISTS bar1();
NOTICE: &nbsp;function bar1() does not exist, skipping
DROP FUNCTION
postgres=# &nbsp; DROP TYPE IF EXISTS foo1 CASCADE;
NOTICE: &nbsp;type "foo1" does not exist, skipping
DROP TYPE
postgres=# &nbsp; CREATE TYPE foo1 AS (a INT, b INT);
CREATE TYPE
postgres=# &nbsp; CREATE FUNCTION bar1(OUT r1 foo1) AS $$
postgres$# &nbsp; BEGIN
postgres$# &nbsp; &nbsp; &nbsp; r1 := ROW(1, 2);
postgres$# &nbsp; &nbsp; &nbsp; ALTER TYPE foo1 ALTER ATTRIBUTE b TYPE TEXT;
postgres$# &nbsp; &nbsp; &nbsp; RETURN;
postgres$# &nbsp; END;
postgres$# &nbsp; $$ LANGUAGE plpgsql;
CREATE FUNCTION
postgres=# SELECT bar1();
&nbsp;bar1
-------
&nbsp;(1,2)
(1 row)

postgres=# DROP FUNCTION IF EXISTS bar2();
NOTICE: &nbsp;function bar2() does not exist, skipping
DROP FUNCTION
postgres=# &nbsp; DROP TYPE IF EXISTS foo2 CASCADE;
NOTICE: &nbsp;type "foo2" does not exist, skipping
DROP TYPE
postgres=# &nbsp; CREATE TYPE foo2 AS (a INT, b TEXT);
CREATE TYPE
postgres=# &nbsp; CREATE FUNCTION bar2() RETURNS foo2 AS $$
postgres$# &nbsp; DECLARE
postgres$# &nbsp; &nbsp; &nbsp; r foo2 := ROW(1, 'hello');
postgres$# &nbsp; BEGIN
postgres$# &nbsp; &nbsp; &nbsp; ALTER TYPE foo2 ALTER ATTRIBUTE b TYPE INT;
postgres$# &nbsp; &nbsp; &nbsp; RETURN r;
postgres$# &nbsp; END;
postgres$# &nbsp; $$ LANGUAGE plpgsql;
CREATE FUNCTION
postgres=# &nbsp; SELECT bar2();
ERROR: &nbsp;invalid input syntax for type integer: "hello"
CONTEXT: &nbsp;PL/pgSQL function bar2() line 6 at RETURN
postgres=# DROP FUNCTION bar();
DROP FUNCTION
postgres=# &nbsp; DROP FUNCTION bar1();
DROP FUNCTION
postgres=# &nbsp; DROP FUNCTION bar2();
DROP FUNCTION
postgres=# &nbsp; DROP TYPE IF EXISTS foo CASCADE;
DROP TYPE
postgres=# &nbsp; DROP TYPE IF EXISTS foo1 CASCADE;
DROP TYPE
postgres=# &nbsp; DROP TYPE IF EXISTS foo2 CASCADE;
DROP TYPE
postgres=# quit

Regards,
Surya Poondla

#13surya poondla
suryapoondla4@gmail.com
In reply to: songjinzhou (#12)
Re: BUG #19382: Server crash at __nss_database_lookup

Hi Songjinzhou,

Thank you for reviewing the patch.

You're correct that there is a minor inefficiency, once a
tupDesc_identifier version mismatch is detected, the patch allocates
new_values/new_nulls arrays and calls deconstruct_expanded_record() before
knowing whether any field types actually differ.
If the version changed but no field types changed (e.g., a constraint-only
ALTER), we do unnecessary work and hit the if (!need_conversion) return
late.

That said, your suggestion is clean and correct. I can restructure the code
to do a first pass over the TupleDesc attributes (which is pure metadata,
no deconstruction needed) to set need_conversion,
and only proceed with deconstruct_expanded_record() and array allocation if
that returns true. This avoids any unnecessary memory allocation in that
intermediate case.

I'll post an updated patch with this improvement.

Thanks again for the careful review!

Regards,
Surya Poondla

#14Andrey Borodin
amborodin@acm.org
In reply to: surya poondla (#13)
Re: BUG #19382: Server crash at __nss_database_lookup

Hi!

Thanks for working on this!

On 20 Mar 2026, at 23:16, surya poondla <suryapoondla4@gmail.com> 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.

In my opinion in both cases (PL/pgSQL + CURSOR) we should error out instead of trying to remediate type changes.

Best regards, Andrey Borodin.

#15surya poondla
suryapoondla4@gmail.com
In reply to: Andrey Borodin (#14)
Re: BUG #19382: Server crash at __nss_database_lookup

Hi All,

Thanks for the review Andrey.

On Thu, Apr 2, 2026 at 4:18 AM Andrey Borodin <x4mmm@yandex-team.ru> wrote:

Hi!

Thanks for working on this!

On 20 Mar 2026, at 23:16, surya poondla <suryapoondla4@gmail.com> 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:

0004-Fix-bug-19382-server-crash-when-ALTER-TYPE-is-used-m.patchapplication/octet-stream; name=0004-Fix-bug-19382-server-crash-when-ALTER-TYPE-is-used-m.patchDownload+68-2
#16Andrey Borodin
amborodin@acm.org
In reply to: surya poondla (#15)
Re: BUG #19382: Server crash at __nss_database_lookup

On 3 Apr 2026, at 04:14, surya poondla <suryapoondla4@gmail.com> wrote:

<0004-Fix-bug-19382-server-crash-when-ALTER-TYPE-is-used-m.patch>

Hi Surya,

Thanks for the updated patch. I noticed it checks er_tupdesc_id of the
outermost record variable, but does not recurse into nested composite types.
The server still crashes when only an inner type is altered:

CREATE TYPE inner_t AS (x INT, y INT);
CREATE TYPE outer_t AS (a INT, b inner_t);

CREATE OR REPLACE FUNCTION test_nested() RETURNS record LANGUAGE plpgsql AS $$
DECLARE r1 outer_t; r2 outer_t;
BEGIN
r1 := ROW(1, ROW(10, power(2,30)::int4)::inner_t)::outer_t;
ALTER TYPE inner_t ALTER ATTRIBUTE y TYPE TEXT;
r2 := r1;
RETURN r2;
END; $$;

SELECT test_nested(); -- server crash

The same gap exists on the cursor side independently of your patch, and I
have a fix for that part that walks the type tree recursively. IMO the
PL/pgSQL assignment path will need a similar recursive check.

I'm definitely not a big fan of checking types on every FETCH, but I see no
other ways around.

Best regards, Andrey Borodin.

Attachments:

v2026-04-04-0001-Fix-incorrect-results-when-composite-typ.patchapplication/octet-stream; name=v2026-04-04-0001-Fix-incorrect-results-when-composite-typ.patch; x-unix-mode=0644Download+225-1