transformJsonParseArg() was not careful enough on generation of
transformed expressions when starting from expressions that are not
coercible to text but are in the string type category: it failed to
verify that coerce_to_target_type() succeeds, and returned a NULL
pointer. This leads to a later NULL dereference and crash at executor
time.
This escaped noticed because it cannot happen for built-in types, all of
which have casts to text. Only user-created types are potentially
problematic.
Fix by raising an error when a cast to text doesn't exist.
This mistake came in with commit
6ee30209a6f1.
Author: Ayush Tiwari <ayushtiwari.slg01@gmail.com>
Reported-by: Chi Zhang <798604270@qq.com>
Reviewed-by: Srinath Reddy Sadipiralla <srinath2133@gmail.com>
Backpatch-through: 16
Discussion: https://postgr.es/m/19491-
7aafc221ec63f288@postgresql.org
{
JsonIsPredicate *n = makeNode(JsonIsPredicate);
+ Assert(expr != NULL);
+
n->expr = expr;
n->format = format;
n->item_type = item_type;
if (*exprtype == UNKNOWNOID || typcategory == TYPCATEGORY_STRING)
{
+ int location = exprLocation(expr);
+
expr = coerce_to_target_type(pstate, expr, *exprtype,
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, location));
+
*exprtype = TEXTOID;
}
-- 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)
+
+\set VERBOSITY terse
+DROP TYPE sqljson_mystr CASCADE;
+NOTICE: drop cascades to 3 other objects
+\set VERBOSITY default
-- view creation and deparsing with domain IS JSON
CREATE VIEW domain_isjson AS
WITH cte(a) AS (VALUES('{"a": 1, "a": 2}'))
-- 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;
+\set VERBOSITY terse
+DROP TYPE sqljson_mystr CASCADE;
+\set VERBOSITY default
+
-- view creation and deparsing with domain IS JSON
CREATE VIEW domain_isjson AS
WITH cte(a) AS (VALUES('{"a": 1, "a": 2}'))