public inbox for [email protected]  
help / color / mirror / Atom feed
From: Ayush Tiwari <[email protected]>
To: Srinath Reddy Sadipiralla <[email protected]>
Cc: [email protected]
Cc: [email protected]
Cc: Álvaro Herrera <[email protected]>
Subject: Re: BUG #19491: Segmentation fault triggered by IS NULL
Date: Fri, 5 Jun 2026 21:41:34 +0530
Message-ID: <CAJTYsWX3wF0Mm0TX4iMdRvtUHmVeLnARzQXzQAKVxmH+m3zE9g@mail.gmail.com> (raw)
In-Reply-To: <CAFC+b6q8GikS7NdDq8sC-Au4j7WZshUtw4Gzw78j0umxJ-LTqg@mail.gmail.com>
References: <[email protected]>
	<CAJTYsWXsDYpYvOs5ZC48ziEkyziy0j7Km023U8u9cLqMd1poug@mail.gmail.com>
	<CAFC+b6q8GikS7NdDq8sC-Au4j7WZshUtw4Gzw78j0umxJ-LTqg@mail.gmail.com>

Hi,

On Mon, 25 May 2026 at 22:06, Srinath Reddy Sadipiralla <
[email protected]> 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


Attachments:

  [application/octet-stream] v2-0001-Avoid-NULL-IS-JSON-expressions-after-failed-text-.patch (5.7K, 3-v2-0001-Avoid-NULL-IS-JSON-expressions-after-failed-text-.patch)
  download | inline diff:
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Ayush Tiwari <[email protected]>
Date: Fri, 5 Jun 2026 16:05:00 +0000
Subject: [PATCH v2] Avoid NULL SQL/JSON expressions after failed text coercion

transformJsonParseArg() tries to coerce UNKNOWN and string-category input
types to text before building SQL/JSON parse and IS JSON expressions.  It did
not check whether coerce_to_target_type() succeeded before replacing the
expression and setting exprtype to TEXTOID.

For a user-defined string-category type without an implicit cast to text,
coerce_to_target_type() returns NULL.  IS JSON then stores that NULL as the
predicate subject, and the executor crashes while initializing expression
state.

Raise a normal coercion error when the implicit cast to text cannot be
created.  This avoids the crash and reports the failure at the point where
the parser decided text coercion was required.  The same helper is used for
JSON() WITH UNIQUE KEYS, so cover that path as well.

Reported-by: Chi Zhang <[email protected]>
Reviewed-by: Srinath Reddy Sadipiralla <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index f1003e57fb2..db5ecf80b43 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -4199,10 +4199,21 @@ transformJsonParseArg(ParseState *pstate, Node *jsexpr, JsonFormat *format,
 
 		if (*exprtype == UNKNOWNOID || typcategory == TYPCATEGORY_STRING)
 		{
-			expr = coerce_to_target_type(pstate, expr, *exprtype,
+			Node	   *coerced_expr;
+
+			coerced_expr = coerce_to_target_type(pstate, expr, *exprtype,
 										 TEXTOID, -1,
 										 COERCION_IMPLICIT,
 										 COERCE_IMPLICIT_CAST, -1);
+			if (coerced_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(expr))));
+
+			expr = coerced_expr;
 			*exprtype = TEXTOID;
 		}
 
diff --git a/src/test/regress/expected/sqljson.out b/src/test/regress/expected/sqljson.out
index 143d961c077..6dc0abc9c5c 100644
--- a/src/test/regress/expected/sqljson.out
+++ b/src/test/regress/expected/sqljson.out
@@ -1470,6 +1470,45 @@ LINE 1: SELECT NULL::jd5 IS JSON WITH UNIQUE KEYS;
 -- domain constraint violation during cast
 SELECT a::jd2 IS JSON WITH UNIQUE KEYS as col1 FROM (VALUES('{"a": 1, "a": 2}')) s(a); -- error
 ERROR:  value for domain jd2 violates check constraint "jd2_check"
+-- A user-defined string-category type with no implicit cast to text must
+-- produce a clean error rather than crash for IS JSON / JSON() input
+-- (per bug #19491).
+CREATE FUNCTION sqljson_mystr_in(cstring) RETURNS sqljson_mystr
+	AS 'textin' LANGUAGE internal IMMUTABLE STRICT;
+NOTICE:  type "sqljson_mystr" is not yet defined
+DETAIL:  Creating a shell type definition.
+CREATE FUNCTION sqljson_mystr_out(sqljson_mystr) RETURNS cstring
+	AS 'textout' LANGUAGE internal IMMUTABLE STRICT;
+NOTICE:  argument type sqljson_mystr is only a shell
+LINE 1: CREATE FUNCTION sqljson_mystr_out(sqljson_mystr) RETURNS cst...
+                                          ^
+CREATE TYPE sqljson_mystr (
+	INPUT = sqljson_mystr_in,
+	OUTPUT = sqljson_mystr_out,
+	LIKE = text,
+	CATEGORY = 'S'
+);
+SELECT '{"a":1}'::sqljson_mystr IS JSON;                -- error
+ERROR:  cannot cast type sqljson_mystr to text
+LINE 1: SELECT '{"a":1}'::sqljson_mystr IS JSON;
+               ^
+SELECT JSON('{"a":1}'::sqljson_mystr WITH UNIQUE KEYS); -- error
+ERROR:  cannot cast type sqljson_mystr to text
+LINE 1: SELECT JSON('{"a":1}'::sqljson_mystr WITH UNIQUE KEYS);
+                    ^
+-- An implicit cast to text lets the same query work normally.
+CREATE CAST (sqljson_mystr AS text) WITHOUT FUNCTION AS IMPLICIT;
+SELECT '{"a":1}'::sqljson_mystr IS JSON;
+ ?column? 
+----------
+ t
+(1 row)
+
+DROP TYPE sqljson_mystr CASCADE;
+NOTICE:  drop cascades to 3 other objects
+DETAIL:  drop cascades to function sqljson_mystr_in(cstring)
+drop cascades to function sqljson_mystr_out(sqljson_mystr)
+drop cascades to cast from sqljson_mystr to text
 -- view creation and deparsing with domain IS JSON
 CREATE VIEW domain_isjson AS
 WITH cte(a) AS (VALUES('{"a": 1, "a": 2}'))
diff --git a/src/test/regress/sql/sqljson.sql b/src/test/regress/sql/sqljson.sql
index ed044d81fdd..689af6f0681 100644
--- a/src/test/regress/sql/sqljson.sql
+++ b/src/test/regress/sql/sqljson.sql
@@ -559,6 +559,26 @@ SELECT NULL::jd5 IS JSON WITH UNIQUE KEYS; -- error
 -- domain constraint violation during cast
 SELECT a::jd2 IS JSON WITH UNIQUE KEYS as col1 FROM (VALUES('{"a": 1, "a": 2}')) s(a); -- error
 
+-- A user-defined string-category type with no implicit cast to text must
+-- produce a clean error rather than crash for IS JSON / JSON() input
+-- (per bug #19491).
+CREATE FUNCTION sqljson_mystr_in(cstring) RETURNS sqljson_mystr
+	AS 'textin' LANGUAGE internal IMMUTABLE STRICT;
+CREATE FUNCTION sqljson_mystr_out(sqljson_mystr) RETURNS cstring
+	AS 'textout' LANGUAGE internal IMMUTABLE STRICT;
+CREATE TYPE sqljson_mystr (
+	INPUT = sqljson_mystr_in,
+	OUTPUT = sqljson_mystr_out,
+	LIKE = text,
+	CATEGORY = 'S'
+);
+SELECT '{"a":1}'::sqljson_mystr IS JSON;                -- error
+SELECT JSON('{"a":1}'::sqljson_mystr WITH UNIQUE KEYS); -- error
+-- An implicit cast to text lets the same query work normally.
+CREATE CAST (sqljson_mystr AS text) WITHOUT FUNCTION AS IMPLICIT;
+SELECT '{"a":1}'::sqljson_mystr IS JSON;
+DROP TYPE sqljson_mystr CASCADE;
+
 -- view creation and deparsing with domain IS JSON
 CREATE VIEW domain_isjson AS
 WITH cte(a) AS (VALUES('{"a": 1, "a": 2}'))


view thread (7+ messages)  latest in thread

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 #19491: Segmentation fault triggered by IS NULL
  In-Reply-To: <CAJTYsWX3wF0Mm0TX4iMdRvtUHmVeLnARzQXzQAKVxmH+m3zE9g@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