static void transformJsonPassingArgs(ParseState *pstate, const char *constructName,
JsonFormatType format, List *args,
List **passing_values, List **passing_names);
-static JsonBehavior *transformJsonBehavior(ParseState *pstate, JsonBehavior *behavior,
+static JsonBehavior *transformJsonBehavior(ParseState *pstate, JsonExpr *jsexpr,
+ JsonBehavior *behavior,
JsonBehaviorType default_behavior,
JsonReturning *returning);
static Node *GetJsonBehaviorConst(JsonBehaviorType btype, int location);
{
jsexpr->returning->typid = BOOLOID;
jsexpr->returning->typmod = -1;
+ jsexpr->collation = InvalidOid;
}
/* JSON_TABLE() COLUMNS can specify a non-boolean type. */
if (jsexpr->returning->typid != BOOLOID)
jsexpr->use_json_coercion = true;
- jsexpr->on_error = transformJsonBehavior(pstate, func->on_error,
+ jsexpr->on_error = transformJsonBehavior(pstate,
+ jsexpr,
+ func->on_error,
JSON_BEHAVIOR_FALSE,
jsexpr->returning);
break;
ret->typmod = -1;
}
+ jsexpr->collation = get_typcollation(jsexpr->returning->typid);
+
/*
* Keep quotes on scalar strings by default, omitting them only if
* OMIT QUOTES is specified.
jsexpr->use_json_coercion = true;
/* Assume NULL ON EMPTY when ON EMPTY is not specified. */
- jsexpr->on_empty = transformJsonBehavior(pstate, func->on_empty,
+ jsexpr->on_empty = transformJsonBehavior(pstate,
+ jsexpr,
+ func->on_empty,
JSON_BEHAVIOR_NULL,
jsexpr->returning);
/* Assume NULL ON ERROR when ON ERROR is not specified. */
- jsexpr->on_error = transformJsonBehavior(pstate, func->on_error,
+ jsexpr->on_error = transformJsonBehavior(pstate,
+ jsexpr,
+ func->on_error,
JSON_BEHAVIOR_NULL,
jsexpr->returning);
break;
jsexpr->returning->typid = TEXTOID;
jsexpr->returning->typmod = -1;
}
+ jsexpr->collation = get_typcollation(jsexpr->returning->typid);
/*
* Override whatever transformJsonOutput() set these to, which
}
/* Assume NULL ON EMPTY when ON EMPTY is not specified. */
- jsexpr->on_empty = transformJsonBehavior(pstate, func->on_empty,
+ jsexpr->on_empty = transformJsonBehavior(pstate,
+ jsexpr,
+ func->on_empty,
JSON_BEHAVIOR_NULL,
jsexpr->returning);
/* Assume NULL ON ERROR when ON ERROR is not specified. */
- jsexpr->on_error = transformJsonBehavior(pstate, func->on_error,
+ jsexpr->on_error = transformJsonBehavior(pstate,
+ jsexpr,
+ func->on_error,
JSON_BEHAVIOR_NULL,
jsexpr->returning);
break;
jsexpr->returning->typid = exprType(jsexpr->formatted_expr);
jsexpr->returning->typmod = -1;
}
+ jsexpr->collation = get_typcollation(jsexpr->returning->typid);
/*
* Assume EMPTY ARRAY ON ERROR when ON ERROR is not specified.
* ON EMPTY cannot be specified at the top level but it can be for
* the individual columns.
*/
- jsexpr->on_error = transformJsonBehavior(pstate, func->on_error,
+ jsexpr->on_error = transformJsonBehavior(pstate,
+ jsexpr,
+ func->on_error,
JSON_BEHAVIOR_EMPTY_ARRAY,
jsexpr->returning);
break;
* Transform a JSON BEHAVIOR clause.
*/
static JsonBehavior *
-transformJsonBehavior(ParseState *pstate, JsonBehavior *behavior,
+transformJsonBehavior(ParseState *pstate, JsonExpr *jsexpr,
+ JsonBehavior *behavior,
JsonBehaviorType default_behavior,
JsonReturning *returning)
{
location = behavior->location;
if (btype == JSON_BEHAVIOR_DEFAULT)
{
+ Oid targetcoll = jsexpr->collation;
+ Oid exprcoll;
+
expr = transformExprRecurse(pstate, behavior->expr);
+
if (!ValidJsonBehaviorDefaultExpr(expr, NULL))
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("DEFAULT expression must not return a set"),
parser_errposition(pstate, exprLocation(expr))));
+
+ /*
+ * Reject a DEFAULT expression whose collation differs from the
+ * enclosing JSON expression's result collation
+ * (jsexpr->collation), as chosen by the RETURNING clause.
+ */
+ exprcoll = exprCollation(expr);
+ if (!OidIsValid(exprcoll))
+ exprcoll = get_typcollation(exprType(expr));
+ if (OidIsValid(targetcoll) && OidIsValid(exprcoll) &&
+ targetcoll != exprcoll)
+ ereport(ERROR,
+ errcode(ERRCODE_COLLATION_MISMATCH),
+ errmsg("the collation of DEFAULT expression conflicts with RETURNING clause"),
+ errdetail("\"%s\" versus \"%s\"",
+ get_collation_name(exprcoll),
+ get_collation_name(targetcoll)),
+ parser_errposition(pstate, exprLocation(expr)));
}
}
3 | d1 | d1
(3 rows)
+-- Check that DEFAULT expressions in SQL/JSON functions use the same collation
+-- as the RETURNING type. Mismatched collations should raise an error.
+CREATE DOMAIN d1 AS text COLLATE case_insensitive;
+CREATE DOMAIN d2 AS text COLLATE "C";
+SELECT JSON_VALUE('{"a": "A"}', '$.a' RETURNING d1 DEFAULT ('C' COLLATE "C") COLLATE case_insensitive ON EMPTY) = 'a'; -- true
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT JSON_VALUE('{"a": "A"}', '$.a' RETURNING d1 DEFAULT 'C' ON EMPTY) = 'a'; -- true
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT JSON_VALUE('{"a": "A"}', '$.a' RETURNING d1 DEFAULT 'C'::d2 ON EMPTY) = 'a'; -- error
+ERROR: the collation of DEFAULT expression conflicts with RETURNING clause
+LINE 1: ...ON_VALUE('{"a": "A"}', '$.a' RETURNING d1 DEFAULT 'C'::d2 ON...
+ ^
+DETAIL: "C" versus "case_insensitive"
+SELECT JSON_VALUE('{"a": "A"}', '$.a' RETURNING d1 DEFAULT 'C' COLLATE "C" ON EMPTY) = 'a'; -- error
+ERROR: the collation of DEFAULT expression conflicts with RETURNING clause
+LINE 1: ...ON_VALUE('{"a": "A"}', '$.a' RETURNING d1 DEFAULT 'C' COLLAT...
+ ^
+DETAIL: "C" versus "case_insensitive"
+SELECT JSON_VALUE('{"a": "A"}', '$.c' RETURNING d1 DEFAULT 'A' ON EMPTY) = 'a'; -- true
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT JSON_VALUE('{"a": "A"}', '$.c' RETURNING d1 DEFAULT 'A' COLLATE case_insensitive ON EMPTY) = 'a'; -- true
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT JSON_VALUE('{"a": "A"}', '$.c' RETURNING d1 DEFAULT 'A'::d2 ON EMPTY) = 'a'; -- error
+ERROR: the collation of DEFAULT expression conflicts with RETURNING clause
+LINE 1: ...ON_VALUE('{"a": "A"}', '$.c' RETURNING d1 DEFAULT 'A'::d2 ON...
+ ^
+DETAIL: "C" versus "case_insensitive"
+SELECT JSON_VALUE('{"a": "A"}', '$.c' RETURNING d1 DEFAULT 'A' COLLATE "C" ON EMPTY) = 'a'; -- error
+ERROR: the collation of DEFAULT expression conflicts with RETURNING clause
+LINE 1: ...ON_VALUE('{"a": "A"}', '$.c' RETURNING d1 DEFAULT 'A' COLLAT...
+ ^
+DETAIL: "C" versus "case_insensitive"
+DROP DOMAIN d1, d2;
-- cleanup
RESET search_path;
SET client_min_messages TO warning;