Re: BUG #19384: Server crash at textout

From: Jeevan Chalke <jeevan(dot)chalke(at)enterprisedb(dot)com>
To: vaibhave postgres <postgresvaibhave(at)gmail(dot)com>
Cc: Pierre Forstmann <pierre(dot)forstmann(at)gmail(dot)com>, Rahila Syed <rahilasyed90(at)gmail(dot)com>, dllggyx(at)outlook(dot)com, pgsql-bugs(at)lists(dot)postgresql(dot)org
Subject: Re: BUG #19384: Server crash at textout
Date: 2026-10-29 13:24:02
Message-ID: CAM2+6=X5FK3SW+d4ybSCmvd+d79Y3_oF3GorQaLfd5GGMT+b9Q@mail.gmail.com
Views: Whole Thread | Raw Message | Download mbox | Resend email
Thread:
Lists: pgsql-bugs

Hi,

I have checked the reproducer test and observed that it still crashes the
server on unpatched master. A crash is not good and needs to be fixed as
soon as possible. Hence, I reviewed the patch on current master. The patch
fixes the crash, but "make check" fails "indexing" (1 of 239), and
"rowtypes" passes only because its expected output was edited.

Here are further observations:

1. Spurious "type ... has changed" with no change to the type:

create table tt (a int, b int); begin;
declare c1 cursor for select (i,1)::tt from generate_series(1,3) i;
fetch c1; create index tt_a on tt(a); fetch c1; -- ERROR

compute_tupdesc_identifier() hashes tdtypmod, but load_typcache_tupdesc()
then overwrites tdtypmod with the low 31 bits of the hash. After a reload
the input differs, so the identifier and marker change although the type
did not. tdtypmod and tdtypeid should not be hash inputs. This breaks the
existing "indexing" test. Also, typentry->tupDesc is the relcache's rd_att
itself, so stamping it makes marker presence depend on cache load order.

2. Supported ALTER TYPE commands now break stored data. On master these
work; with the patch each makes the next SELECT fail:

create type pt as (x int, y int); create table tpt (id int, p pt);
insert into tpt values (1, row(1,2)::pt);
alter type pt add attribute z int; -- or rename/drop attribute
select * from tpt; -- ERROR

The hash covers names, natts, attnotnull, attstorage, etc., none of which
changes how an existing datum's bytes are laid out. The rowtypes.out
change (ALTER TABLE fullname ADD COLUMN, "but this should work:") hides
this; it should be reverted and fixed instead.

3. tupDesc_identifier must be a unique identifier that changes whenever the
TupleDesc changes. However, it is now set to a hash value that skips
various fields like atthasdef, attislocal and attinhcount. So it is
possible that the relcache installs a new TupleDesc with the same
identifier. Given this, it would be good to leave it untouched here. What
if we just create a 32 bit hash and store that in tdtypmod, which is used
for comparing the stale datum anyway?

4. The marker becomes part of the on-disk datum format (values can be
stored in heap tuples and indexes), so the hash and its field list become a
pg_upgrade/replication compatibility surface. What guarantees and evolution
policy are intended?

5. Have you analyzed whether it is safe to overload tdtypmod? I am not
sure, but since it is used widely, we need to be extra sure about its usage.

Regards,

On Sun, Jan 25, 2026 at 4:17 PM vaibhave postgres <
postgresvaibhave(at)gmail(dot)com> wrote:

> Hey,
>
> I replaced the `tupDesc_identifier` with a 64-bit signature from its
> structure. Stored Datums can detect stale definitions.
>
> On Tue, Jan 20, 2026 at 8:33 PM Pierre Forstmann <
> pierre(dot)forstmann(at)gmail(dot)com> wrote:
>
>> I can also reproduce with 18.1.
>>
>> But if type foo is used in a table, type foo cannot be modified:
>> DROP TYPE IF EXISTS foo CASCADE;
>> psql:bug4.sql:1: NOTICE: drop cascades to column c of table bar
>> DROP TYPE
>> DROP TABLE if EXISTS bar CASCADE;
>> DROP TABLE
>> CREATE TYPE foo AS (a INT, b INT);
>> CREATE TYPE
>> CREATE TABLE bar(c foo);
>> CREATE TABLE
>> BEGIN;
>> BEGIN
>> INSERT into bar SELECT (i, power(2, 30))::foo FROM generate_series(1,10)
>> i;
>> INSERT 0 10
>> ALTER TYPE foo ALTER ATTRIBUTE b TYPE TEXT;
>> psql:bug4.sql:8: ERROR: cannot alter type "foo" because column "bar.c"
>> uses it
>>
>> Should PG allow to modify a type if this type is used to cast a SELECT
>> list column in the same transaction ?
>>
>>
>> Le 20/01/2026 à 12:15, Rahila Syed a écrit :
>>
>> Hi,
>>
>> On Tue, Jan 20, 2026 at 2:29 PM PG Bug reporting form <
>> noreply(at)postgresql(dot)org> wrote:
>>
>>> The following bug has been logged on the website:
>>>
>>> Bug reference: 19384
>>> Logged by: Yuxiao Guo
>>> Email address: dllggyx(at)outlook(dot)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 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;
>>>
>>>
>>> Stacktrace:
>>> #0 0x7ae1c818a00b (gsignal+0xcb)
>>> #1 0x7ae1c8169859 (abort+0x12b)
>>> #2 0x542fa7 (_ZN11__sanitizer5AbortEv+0x47)
>>> #3 0x5414d1 (_ZN11__sanitizer3DieEv+0xc1)
>>> #4 0x528a14 (_ZN6__asan19ScopedInErrorReportD2Ev+0x1c4)
>>> #5 0x52a5da (_ZN6__asan18ReportGenericErrorEmmmmbmjb+0x5ba)
>>> #6 0x523ef6 (__asan_memcpy+0x1d6)
>>> #7 0x17772d5 (textout+0x1b5)
>>> #8 0x1835834 (OutputFunctionCall+0x174)
>>> #9 0x167a568 (record_out+0x828)
>>> #10 0x1835834 (OutputFunctionCall+0x174)
>>> #11 0x595848 (printtup+0x958)
>>> #12 0x1336280 (RunFromStore+0x1d0)
>>> #13 0x1333ec0 (PortalRunSelect+0x150)
>>> #14 0x133321d (PortalRun+0x51d)
>>> #15 0x132f1de (exec_simple_query+0x146e)
>>> #16 0x1328627 (PostgresMain+0x2c57)
>>> #17 0x13192e4 (BackendMain+0xe4)
>>> #18 0x10a26c3 (postmaster_child_launch+0x193)
>>> #19 0x10adb91 (ServerLoop+0x4821)
>>> #20 0x10a76ec (PostmasterMain+0x241c)
>>> #21 0xd5c2b8 (main+0x458)
>>> #22 0x7ae1c816b083 (__libc_start_main+0xf3)
>>> #23 0x4a9c6e (_start+0x2e)
>>>
>>>
>>>
>> This problem is reproducible, also the issue seems to be linked to cursors
>> since the type cast with only SELECT statements runs fine.
>>
>> CREATE TYPE foo AS (a INT, b INT);
>> ALTER TYPE foo ALTER ATTRIBUTE b TYPE TEXT;
>>
>> postgres=# SELECT (i, power(2, 30))::foo FROM
>> generate_series(1,10) i;
>> row
>> -----------------
>> (1,1073741824)
>> (2,1073741824)
>> (3,1073741824)
>> (4,1073741824)
>> (5,1073741824)
>> (6,1073741824)
>> (7,1073741824)
>> (8,1073741824)
>> (9,1073741824)
>> (10,1073741824)
>> (10 rows)
>>
>> Also, it happens only if ALTER TYPE to TEXT is run after DECLARING the
>> cursor.
>>
>> Another observation is that when I lower the exponent to 10, the crash no
>> longer occurs,
>> but the output is different.
>>
>> CREATE TYPE foo AS (a INT, b INT);
>> BEGIN;
>> DECLARE c CURSOR FOR SELECT (i, power(2, 10))::foo FROM
>> generate_series(1,10) i;
>> DECLARE CURSOR
>> FETCH c;
>> row
>> ----------
>> (1,1024)
>> (1 row)
>>
>> ALTER TYPE foo ALTER ATTRIBUTE b TYPE TEXT;
>> FETCH c;
>> row
>> ----------
>> (2,\x10)
>> (1 row)
>>
>> Thank you,
>> Rahila Syed
>>
>>

--
*Jeevan Chalke*
*Senior Principal Engineer, Engineering Manager*
*Product Development*

enterprisedb.com <https://www.enterprisedb.com>

In response to

Browse pgsql-bugs by date

  From Date Subject
Previous Message Fujii Masao 2026-10-29 11:47:43 Re: START_REPLICATION silently truncates an overlong LSN component