Thread

Commits

  1. IS JSON/JSON(): Protect against expressions uncoercible to text

  2. Reject degenerate SPLIT PARTITION with DEFAULT partition

  1. BUG #19491: Segmentation fault triggered by IS NULL

    The Post Office <noreply@postgresql.org> — 2026-05-22T06:27:28Z

    The following bug has been logged on the website:
    
    Bug reference:      19491
    Logged by:          Chi Zhang
    Email address:      798604270@qq.com
    PostgreSQL version: 18.4
    Operating system:   Ubuntu 24.04
    Description:        
    
    Hi,
    
    I found that the following test case triggers a segmentation fault.
    
    ```
    CREATE SCHEMA IF NOT EXISTS poc;
    
    CREATE FUNCTION poc.mystring_in(cstring)
        RETURNS poc.mystring
        AS 'textin' LANGUAGE internal IMMUTABLE STRICT;
    
    CREATE FUNCTION poc.mystring_out(poc.mystring)
        RETURNS cstring
        AS 'textout' LANGUAGE internal IMMUTABLE STRICT;
    
    CREATE TYPE poc.mystring (
        INPUT = poc.mystring_in,
        OUTPUT = poc.mystring_out,
        LIKE = text,
        CATEGORY = 'S'
    );
    
    SELECT '{"a":1}'::poc.mystring IS JSON;
    
    DROP SCHEMA IF EXISTS poc CASCADE;
    ```
    
    This is the output:
    
    ```
    sqlancer=# CREATE SCHEMA IF NOT EXISTS poc;
    CREATE SCHEMA
    sqlancer=# CREATE FUNCTION poc.mystring_in(cstring)
        RETURNS poc.mystring
        AS 'textin' LANGUAGE internal IMMUTABLE STRICT;
    NOTICE:  type "poc.mystring" is not yet defined
    DETAIL:  Creating a shell type definition.
    CREATE FUNCTION
    sqlancer=# CREATE FUNCTION poc.mystring_out(poc.mystring)
        RETURNS cstring
        AS 'textout' LANGUAGE internal IMMUTABLE STRICT;
    NOTICE:  argument type poc.mystring is only a shell
    LINE 1: CREATE FUNCTION poc.mystring_out(poc.mystring)
                                             ^
    CREATE FUNCTION
    sqlancer=# CREATE TYPE poc.mystring (
        INPUT = poc.mystring_in,
        OUTPUT = poc.mystring_out,
        LIKE = text,
        CATEGORY = 'S'
    );
    CREATE TYPE
    sqlancer=# SELECT '{"a":1}'::poc.mystring IS JSON;
    server closed the connection unexpectedly
            This probably means the server terminated abnormally
            before or while processing the request.
    The connection to the server was lost. Attempting reset: Succeeded.
    ```
    
    This is the log:
    
    ```
    2026-05-22 06:19:00.811 UTC [1] LOG:  starting PostgreSQL 18.1 (Debian
    18.1-1.pgdg13+2) on x86_64-pc-linux-gnu, compiled by gcc (Debian 14.2.0-19)
    14.2.0, 64-bit
    2026-05-22 06:19:00.811 UTC [1] LOG:  listening on IPv4 address "0.0.0.0",
    port 5432
    2026-05-22 06:19:00.811 UTC [1] LOG:  listening on IPv6 address "::", port
    5432
    2026-05-22 06:19:00.816 UTC [1] LOG:  listening on Unix socket
    "/var/run/postgresql/.s.PGSQL.5432"
    2026-05-22 06:19:00.825 UTC [32] LOG:  database system was shut down at
    2026-05-22 06:18:54 UTC
    2026-05-22 06:19:00.830 UTC [1] LOG:  database system is ready to accept
    connections
    2026-05-22 06:21:48.272 UTC [1] LOG:  client backend (PID 42) was terminated
    by signal 11: Segmentation fault
    2026-05-22 06:21:48.272 UTC [1] DETAIL:  Failed process was running: SELECT
    '{"a":1}'::poc.mystring IS JSON;
    2026-05-22 06:21:48.272 UTC [1] LOG:  terminating any other active server
    processes
    2026-05-22 06:21:48.273 UTC [1] LOG:  all server processes terminated;
    reinitializing
    2026-05-22 06:21:48.324 UTC [49] LOG:  database system was interrupted; last
    known up at 2026-05-22 06:19:00 UTC
    2026-05-22 06:21:48.428 UTC [49] LOG:  database system was not properly shut
    down; automatic recovery in progress
    2026-05-22 06:21:48.431 UTC [49] LOG:  redo starts at 0/1C07F50
    2026-05-22 06:21:48.432 UTC [49] LOG:  invalid record length at 0/1C1AE88:
    expected at least 24, got 0
    2026-05-22 06:21:48.432 UTC [49] LOG:  redo done at 0/1C1AE00 system usage:
    CPU: user: 0.00 s, system: 0.00 s, elapsed: 0.00 s
    2026-05-22 06:21:48.437 UTC [50] LOG:  checkpoint starting: end-of-recovery
    immediate wait
    2026-05-22 06:21:48.450 UTC [50] LOG:  checkpoint complete: wrote 15 buffers
    (0.1%), wrote 3 SLRU buffers; 0 WAL file(s) added, 0 removed, 0 recycled;
    write=0.003 s, sync=0.004 s, total=0.016 s; sync files=16, longest=0.002 s,
    average=0.001 s; distance=75 kB, estimate=75 kB; lsn=0/1C1AE88, redo
    lsn=0/1C1AE88
    2026-05-22 06:21:48.455 UTC [1] LOG:  database system is ready to accept
    connections
    ```
    
    I can reproduce this on the github commit
    d8af73010033cb8ad6c941942d6b03d74d7e4f7e
    
    
    
    
    
    
  2. Re: BUG #19491: Segmentation fault triggered by IS NULL

    Ayush Tiwari <ayushtiwari.slg01@gmail.com> — 2026-05-25T15:12:41Z

    Hi,
    
    On Mon, 25 May 2026 at 18:57, PG Bug reporting form <noreply@postgresql.org>
    wrote:
    
    > The following bug has been logged on the website:
    >
    > Bug reference:      19491
    > Logged by:          Chi Zhang
    > Email address:      798604270@qq.com
    > PostgreSQL version: 18.4
    > Operating system:   Ubuntu 24.04
    > Description:
    >
    > Hi,
    >
    > I found that the following test case triggers a segmentation fault.
    >
    > ```
    > CREATE SCHEMA IF NOT EXISTS poc;
    >
    > CREATE FUNCTION poc.mystring_in(cstring)
    >     RETURNS poc.mystring
    >     AS 'textin' LANGUAGE internal IMMUTABLE STRICT;
    >
    > CREATE FUNCTION poc.mystring_out(poc.mystring)
    >     RETURNS cstring
    >     AS 'textout' LANGUAGE internal IMMUTABLE STRICT;
    >
    > CREATE TYPE poc.mystring (
    >     INPUT = poc.mystring_in,
    >     OUTPUT = poc.mystring_out,
    >     LIKE = text,
    >     CATEGORY = 'S'
    > );
    >
    > SELECT '{"a":1}'::poc.mystring IS JSON;
    >
    > DROP SCHEMA IF EXISTS poc CASCADE;
    > ```
    >
    > This is the output:
    >
    > ```
    > sqlancer=# CREATE SCHEMA IF NOT EXISTS poc;
    > CREATE SCHEMA
    > sqlancer=# CREATE FUNCTION poc.mystring_in(cstring)
    >     RETURNS poc.mystring
    >     AS 'textin' LANGUAGE internal IMMUTABLE STRICT;
    > NOTICE:  type "poc.mystring" is not yet defined
    > DETAIL:  Creating a shell type definition.
    > CREATE FUNCTION
    > sqlancer=# CREATE FUNCTION poc.mystring_out(poc.mystring)
    >     RETURNS cstring
    >     AS 'textout' LANGUAGE internal IMMUTABLE STRICT;
    > NOTICE:  argument type poc.mystring is only a shell
    > LINE 1: CREATE FUNCTION poc.mystring_out(poc.mystring)
    >                                          ^
    > CREATE FUNCTION
    > sqlancer=# CREATE TYPE poc.mystring (
    >     INPUT = poc.mystring_in,
    >     OUTPUT = poc.mystring_out,
    >     LIKE = text,
    >     CATEGORY = 'S'
    > );
    > CREATE TYPE
    > sqlancer=# SELECT '{"a":1}'::poc.mystring IS JSON;
    > server closed the connection unexpectedly
    >         This probably means the server terminated abnormally
    >         before or while processing the request.
    > The connection to the server was lost. Attempting reset: Succeeded.
    > ```
    >
    
    Thanks for the report.  I was able to reproduce this on HEAD with your SQL.
    The crash is in the executor while building expression state for the IS JSON
    predicate -- ExecInitExprRec() ends up being called with a NULL node:
    
      #0  ExecInitExprRec(node=0x0, ...)        execExpr.c:966
      #1  ExecInitExprRec(<JsonIsPredicate>)    execExpr.c:2507
      #2  ExecBuildProjectionInfo(...)          execExpr.c:511
    
    The NULL comes from transformJsonParseArg().  For UNKNOWN or string-category
    input types it implicitly coerces to text via coerce_to_target_type() and
    then sets exprtype to TEXTOID  without checking if the coercion succeeded.
    poc.mystring is in CATEGORY = 'S' but has no implicit cast to text, so
    coerce_to_target_type() returns NULL; the parser still claims it's text and
    that NULL ends up as the JsonIsPredicate subject.
    
    String-category alone isn't a promise of text-coercibility -- adding
    CREATE CAST (poc.mystring AS text) ... AS IMPLICIT makes the same query
    works fine.  I guess the parser just shouldn't assume the coercion worked?
    
    The smallest fix I could see is to only update expr / exprtype when
    coerce_to_target_type() returns non-NULL.  Then transformJsonIsPredicate()
    (and the similar JSON() WITH UNIQUE KEYS path) raise their existing
    "cannot use type X" errors instead of crashing:
    
      ERROR:  cannot use type poc.mystring in IS JSON predicate
    
    Attached is a small patch with that fix and a regression test in sqljson.
    
    Blame points at 6ee30209a6f1 (March 2023), so this looks like it goes back
    to v16.
    
    Regards,
    Ayush
    
  3. Re: BUG #19491: Segmentation fault triggered by IS NULL

    Srinath Reddy Sadipiralla <srinath2133@gmail.com> — 2026-05-25T16:36:01Z

    Hi,
    
    On Mon, May 25, 2026 at 8:43 PM Ayush Tiwari <ayushtiwari.slg01@gmail.com>
    wrote:
    
    > Hi,
    >
    > On Mon, 25 May 2026 at 18:57, PG Bug reporting form <
    > noreply@postgresql.org> wrote:
    >
    >> The following bug has been logged on the website:
    >>
    >> Bug reference:      19491
    >> Logged by:          Chi Zhang
    >> Email address:      798604270@qq.com
    >> PostgreSQL version: 18.4
    >> Operating system:   Ubuntu 24.04
    >> Description:
    >>
    >> Hi,
    >>
    >> I found that the following test case triggers a segmentation fault.
    >>
    >> ```
    >> CREATE SCHEMA IF NOT EXISTS poc;
    >>
    >> CREATE FUNCTION poc.mystring_in(cstring)
    >>     RETURNS poc.mystring
    >>     AS 'textin' LANGUAGE internal IMMUTABLE STRICT;
    >>
    >> CREATE FUNCTION poc.mystring_out(poc.mystring)
    >>     RETURNS cstring
    >>     AS 'textout' LANGUAGE internal IMMUTABLE STRICT;
    >>
    >> CREATE TYPE poc.mystring (
    >>     INPUT = poc.mystring_in,
    >>     OUTPUT = poc.mystring_out,
    >>     LIKE = text,
    >>     CATEGORY = 'S'
    >> );
    >>
    >> SELECT '{"a":1}'::poc.mystring IS JSON;
    >>
    >> DROP SCHEMA IF EXISTS poc CASCADE;
    >> ```
    >>
    >> This is the output:
    >>
    >> ```
    >> sqlancer=# CREATE SCHEMA IF NOT EXISTS poc;
    >> CREATE SCHEMA
    >> sqlancer=# CREATE FUNCTION poc.mystring_in(cstring)
    >>     RETURNS poc.mystring
    >>     AS 'textin' LANGUAGE internal IMMUTABLE STRICT;
    >> NOTICE:  type "poc.mystring" is not yet defined
    >> DETAIL:  Creating a shell type definition.
    >> CREATE FUNCTION
    >> sqlancer=# CREATE FUNCTION poc.mystring_out(poc.mystring)
    >>     RETURNS cstring
    >>     AS 'textout' LANGUAGE internal IMMUTABLE STRICT;
    >> NOTICE:  argument type poc.mystring is only a shell
    >> LINE 1: CREATE FUNCTION poc.mystring_out(poc.mystring)
    >>                                          ^
    >> CREATE FUNCTION
    >> sqlancer=# CREATE TYPE poc.mystring (
    >>     INPUT = poc.mystring_in,
    >>     OUTPUT = poc.mystring_out,
    >>     LIKE = text,
    >>     CATEGORY = 'S'
    >> );
    >> CREATE TYPE
    >> sqlancer=# SELECT '{"a":1}'::poc.mystring IS JSON;
    >> server closed the connection unexpectedly
    >>         This probably means the server terminated abnormally
    >>         before or while processing the request.
    >> The connection to the server was lost. Attempting reset: Succeeded.
    >> ```
    >>
    >
    > Thanks for the report.  I was able to reproduce this on HEAD with your SQL.
    > The crash is in the executor while building expression state for the IS
    > JSON
    > predicate -- ExecInitExprRec() ends up being called with a NULL node:
    >
    >   #0  ExecInitExprRec(node=0x0, ...)        execExpr.c:966
    >   #1  ExecInitExprRec(<JsonIsPredicate>)    execExpr.c:2507
    >   #2  ExecBuildProjectionInfo(...)          execExpr.c:511
    >
    > The NULL comes from transformJsonParseArg().  For UNKNOWN or
    > string-category
    > input types it implicitly coerces to text via coerce_to_target_type() and
    > then sets exprtype to TEXTOID  without checking if the coercion succeeded.
    > poc.mystring is in CATEGORY = 'S' but has no implicit cast to text, so
    > coerce_to_target_type() returns NULL; the parser still claims it's text and
    > that NULL ends up as the JsonIsPredicate subject.
    >
    
    yeah , this was the reason.
    
    
    >
    > String-category alone isn't a promise of text-coercibility -- adding
    > CREATE CAST (poc.mystring AS text) ... AS IMPLICIT makes the same query
    > works fine.  I guess the parser just shouldn't assume the coercion worked?
    >
    > The smallest fix I could see is to only update expr / exprtype when
    > coerce_to_target_type() returns non-NULL.  Then transformJsonIsPredicate()
    > (and the similar JSON() WITH UNIQUE KEYS path) raise their existing
    > "cannot use type X" errors instead of crashing:
    >
    >   ERROR:  cannot use type poc.mystring in IS JSON predicate
    >
    
    I have reviewed the patch , i think the error message might be
    something like
    
    diff --git a/src/backend/parser/parse_expr.c
    b/src/backend/parser/parse_expr.c
    index 89d0f348303..59e34f082e4 100644
    --- a/src/backend/parser/parse_expr.c
    +++ b/src/backend/parser/parse_expr.c
    @@ -4203,6 +4203,13 @@ transformJsonParseArg(ParseState *pstate, Node
    *jsexpr, JsonFormat *format,
    
         TEXTOID, -1,
    
         COERCION_IMPLICIT,
    
         COERCE_IMPLICIT_CAST, -1);
    +                       if(expr == NULL)
    +                               ereport(ERROR,
    +                               (errcode(ERRCODE_CANNOT_COERCE),
    +                                errmsg("cannot cast type %s to %s",
    +                                               format_type_be(*exprtype),
    +                                               format_type_be(TEXTOID)),
    +                                parser_errposition(pstate,
    exprLocation(raw_expr))));
                            *exprtype = TEXTOID;
                    }
    
    -- 
    Thanks,
    Srinath Reddy Sadipiralla
    EDB: https://www.enterprisedb.com/
    
  4. Re: BUG #19491: Segmentation fault triggered by IS NULL

    Ayush Tiwari <ayushtiwari.slg01@gmail.com> — 2026-06-05T16:11:34Z

    Hi,
    
    On Mon, 25 May 2026 at 22:06, Srinath Reddy Sadipiralla <
    srinath2133@gmail.com> wrote:
    
    > Hi,
    >
    > I have reviewed the patch , i think the error message might be
    > something like
    >
    > diff --git a/src/backend/parser/parse_expr.c
    > b/src/backend/parser/parse_expr.c
    > index 89d0f348303..59e34f082e4 100644
    > --- a/src/backend/parser/parse_expr.c
    > +++ b/src/backend/parser/parse_expr.c
    > @@ -4203,6 +4203,13 @@ transformJsonParseArg(ParseState *pstate, Node
    > *jsexpr, JsonFormat *format,
    >
    >        TEXTOID, -1,
    >
    >        COERCION_IMPLICIT,
    >
    >        COERCE_IMPLICIT_CAST, -1);
    > +                       if(expr == NULL)
    > +                               ereport(ERROR,
    > +                               (errcode(ERRCODE_CANNOT_COERCE),
    > +                                errmsg("cannot cast type %s to %s",
    > +                                               format_type_be(*exprtype),
    > +                                               format_type_be(TEXTOID)),
    > +                                parser_errposition(pstate,
    > exprLocation(raw_expr))));
    >                         *exprtype = TEXTOID;
    >                 }
    >
    
    Thanks, that reads better.  v2 attached, raising ERRCODE_CANNOT_COERCE
    right where the coercion fails:
    
      ERROR:  cannot cast type sqljson_mystr to text
    
    which also covers the JSON() WITH UNIQUE KEYS path.
    
    Regards,
    Ayush
    
  5. Re: BUG #19491: Segmentation fault triggered by IS NULL

    Srinath Reddy Sadipiralla <srinath2133@gmail.com> — 2026-06-06T06:27:38Z

    Hi,
    
    On Fri, Jun 5, 2026 at 9:41 PM Ayush Tiwari <ayushtiwari.slg01@gmail.com>
    wrote:
    
    > Hi,
    >
    > On Mon, 25 May 2026 at 22:06, Srinath Reddy Sadipiralla <
    > srinath2133@gmail.com> wrote:
    >
    >> Hi,
    >>
    >> I have reviewed the patch , i think the error message might be
    >> something like
    >>
    >> diff --git a/src/backend/parser/parse_expr.c
    >> b/src/backend/parser/parse_expr.c
    >> index 89d0f348303..59e34f082e4 100644
    >> --- a/src/backend/parser/parse_expr.c
    >> +++ b/src/backend/parser/parse_expr.c
    >> @@ -4203,6 +4203,13 @@ transformJsonParseArg(ParseState *pstate, Node
    >> *jsexpr, JsonFormat *format,
    >>
    >>        TEXTOID, -1,
    >>
    >>        COERCION_IMPLICIT,
    >>
    >>        COERCE_IMPLICIT_CAST, -1);
    >> +                       if(expr == NULL)
    >> +                               ereport(ERROR,
    >> +                               (errcode(ERRCODE_CANNOT_COERCE),
    >> +                                errmsg("cannot cast type %s to %s",
    >> +                                               format_type_be(*exprtype),
    >> +                                               format_type_be(TEXTOID)),
    >> +                                parser_errposition(pstate,
    >> exprLocation(raw_expr))));
    >>                         *exprtype = TEXTOID;
    >>                 }
    >>
    >
    > Thanks, that reads better.  v2 attached, raising ERRCODE_CANNOT_COERCE
    > right where the coercion fails:
    >
    >   ERROR:  cannot cast type sqljson_mystr to text
    >
    > which also covers the JSON() WITH UNIQUE KEYS path.
    >
    
    Thanks for the updated patch, LGTM.
    
    -- 
    Thanks :)
    Srinath Reddy Sadipiralla
    EDB: https://www.enterprisedb.com/
    
  6. Re: BUG #19491: Segmentation fault triggered by IS NULL

    Álvaro Herrera <alvherre@kurilemu.de> — 2026-06-08T18:40:20Z

    On 2026-Jun-06, Srinath Reddy Sadipiralla wrote:
    
    > > Thanks, that reads better.  v2 attached, raising ERRCODE_CANNOT_COERCE
    > > right where the coercion fails:
    > >
    > >   ERROR:  cannot cast type sqljson_mystr to text
    > >
    > > which also covers the JSON() WITH UNIQUE KEYS path.
    > 
    > Thanks for the updated patch, LGTM.
    
    Thanks -- will review, hoping to push soon.
    
    -- 
    Álvaro Herrera        Breisgau, Deutschland  —  https://www.EnterpriseDB.com/
    "That sort of implies that there are Emacs keystrokes which aren't obscure.
    I've been using it daily for 2 years now and have yet to discover any key
    sequence which makes any sense."                        (Paul Thomas)
    
    
    
    
  7. Re: BUG #19491: Segmentation fault triggered by IS NULL

    Álvaro Herrera <alvherre@kurilemu.de> — 2026-06-11T14:27:17Z

    On 2026-Jun-06, Srinath Reddy Sadipiralla wrote:
    
    > On Fri, Jun 5, 2026 at 9:41 PM Ayush Tiwari <ayushtiwari.slg01@gmail.com>
    > wrote:
    
    > > Thanks, that reads better.  v2 attached, raising ERRCODE_CANNOT_COERCE
    > > right where the coercion fails:
    > >
    > >   ERROR:  cannot cast type sqljson_mystr to text
    > >
    > > which also covers the JSON() WITH UNIQUE KEYS path.
    > 
    > Thanks for the updated patch, LGTM.
    
    Thank you, I have pushed this to all branches since 16.
    
    I also added an Assert() to makeJsonIsPredicate() that 'expr' is not
    null.  This wouldn't have caught this problem, mind, because what was
    really missing is an example case where the problem appears.  However,
    it seems good manners to disallow that case explicitly rather than
    have it return a Expr node with a embedded null pointer that's only
    going to cause indirect problems.
    
    BTW the subject of this thread is somehow comically bogus.  The problem
    is IS JSON causing a NULL pointer dereference, not IS NULL.
    
    -- 
    Álvaro Herrera        Breisgau, Deutschland  —  https://www.EnterpriseDB.com/