aboutsummaryrefslogtreecommitdiff
path: root/src/backend/parser/parse_expr.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/parser/parse_expr.c')
-rw-r--r--src/backend/parser/parse_expr.c225
1 files changed, 210 insertions, 15 deletions
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index c08c06373a9..fed8e4d0897 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -86,6 +86,10 @@ static Node *transformJsonArrayQueryConstructor(ParseState *pstate,
static Node *transformJsonObjectAgg(ParseState *pstate, JsonObjectAgg *agg);
static Node *transformJsonArrayAgg(ParseState *pstate, JsonArrayAgg *agg);
static Node *transformJsonIsPredicate(ParseState *pstate, JsonIsPredicate *pred);
+static Node *transformJsonParseExpr(ParseState *pstate, JsonParseExpr *expr);
+static Node *transformJsonScalarExpr(ParseState *pstate, JsonScalarExpr *expr);
+static Node *transformJsonSerializeExpr(ParseState *pstate,
+ JsonSerializeExpr *expr);
static Node *make_row_comparison_op(ParseState *pstate, List *opname,
List *largs, List *rargs, int location);
static Node *make_row_distinct_op(ParseState *pstate, List *opname,
@@ -337,6 +341,18 @@ transformExprRecurse(ParseState *pstate, Node *expr)
result = transformJsonIsPredicate(pstate, (JsonIsPredicate *) expr);
break;
+ case T_JsonParseExpr:
+ result = transformJsonParseExpr(pstate, (JsonParseExpr *) expr);
+ break;
+
+ case T_JsonScalarExpr:
+ result = transformJsonScalarExpr(pstate, (JsonScalarExpr *) expr);
+ break;
+
+ case T_JsonSerializeExpr:
+ result = transformJsonSerializeExpr(pstate, (JsonSerializeExpr *) expr);
+ break;
+
default:
/* should not reach here */
elog(ERROR, "unrecognized node type: %d", (int) nodeTag(expr));
@@ -3204,15 +3220,16 @@ makeJsonByteaToTextConversion(Node *expr, JsonFormat *format, int location)
/*
* Transform JSON value expression using specified input JSON format or
- * default format otherwise.
+ * default format otherwise, coercing to the targettype if needed.
*
* Returned expression is either ve->raw_expr coerced to text (if needed) or
* a JsonValueExpr with formatted_expr set to the coerced copy of raw_expr
- * if the specified format requires it.
+ * if the specified format and the targettype requires it.
*/
static Node *
transformJsonValueExpr(ParseState *pstate, const char *constructName,
- JsonValueExpr *ve, JsonFormatType default_format)
+ JsonValueExpr *ve, JsonFormatType default_format,
+ Oid targettype)
{
Node *expr = transformExprRecurse(pstate, (Node *) ve->raw_expr);
Node *rawexpr;
@@ -3254,12 +3271,14 @@ transformJsonValueExpr(ParseState *pstate, const char *constructName,
else
format = default_format;
- if (format != JS_FORMAT_DEFAULT)
+ if (format != JS_FORMAT_DEFAULT ||
+ (OidIsValid(targettype) && exprtype != targettype))
{
- Oid targettype = format == JS_FORMAT_JSONB ? JSONBOID : JSONOID;
Node *coerced;
+ bool only_allow_cast = OidIsValid(targettype);
- if (exprtype != BYTEAOID && typcategory != TYPCATEGORY_STRING)
+ if (!only_allow_cast &&
+ exprtype != BYTEAOID && typcategory != TYPCATEGORY_STRING)
ereport(ERROR,
errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg(ve->format->format_type == JS_FORMAT_DEFAULT ?
@@ -3275,6 +3294,9 @@ transformJsonValueExpr(ParseState *pstate, const char *constructName,
exprtype = TEXTOID;
}
+ if (!OidIsValid(targettype))
+ targettype = format == JS_FORMAT_JSONB ? JSONBOID : JSONOID;
+
/* Try to coerce to the target type. */
coerced = coerce_to_target_type(pstate, expr, exprtype,
targettype, -1,
@@ -3285,11 +3307,24 @@ transformJsonValueExpr(ParseState *pstate, const char *constructName,
if (!coerced)
{
/* If coercion failed, use to_json()/to_jsonb() functions. */
- Oid fnoid = targettype == JSONOID ? F_TO_JSON : F_TO_JSONB;
- FuncExpr *fexpr = makeFuncExpr(fnoid, targettype,
- list_make1(expr),
- InvalidOid, InvalidOid,
- COERCE_EXPLICIT_CALL);
+ FuncExpr *fexpr;
+ Oid fnoid;
+
+ /*
+ * Though only allow a cast when the target type is specified by
+ * the caller.
+ */
+ if (only_allow_cast)
+ ereport(ERROR,
+ (errcode(ERRCODE_CANNOT_COERCE),
+ errmsg("cannot cast type %s to %s",
+ format_type_be(exprtype),
+ format_type_be(targettype)),
+ parser_errposition(pstate, location)));
+
+ fnoid = targettype == JSONOID ? F_TO_JSON : F_TO_JSONB;
+ fexpr = makeFuncExpr(fnoid, targettype, list_make1(expr),
+ InvalidOid, InvalidOid, COERCE_EXPLICIT_CALL);
fexpr->location = location;
@@ -3590,7 +3625,8 @@ transformJsonObjectConstructor(ParseState *pstate, JsonObjectConstructor *ctor)
Node *key = transformExprRecurse(pstate, (Node *) kv->key);
Node *val = transformJsonValueExpr(pstate, "JSON_OBJECT()",
kv->value,
- JS_FORMAT_DEFAULT);
+ JS_FORMAT_DEFAULT,
+ InvalidOid);
args = lappend(args, key);
args = lappend(args, val);
@@ -3776,7 +3812,8 @@ transformJsonObjectAgg(ParseState *pstate, JsonObjectAgg *agg)
key = transformExprRecurse(pstate, (Node *) agg->arg->key);
val = transformJsonValueExpr(pstate, "JSON_OBJECTAGG()",
agg->arg->value,
- JS_FORMAT_DEFAULT);
+ JS_FORMAT_DEFAULT,
+ InvalidOid);
args = list_make2(key, val);
returning = transformJsonConstructorOutput(pstate, agg->constructor->output,
@@ -3834,7 +3871,7 @@ transformJsonArrayAgg(ParseState *pstate, JsonArrayAgg *agg)
arg = transformJsonValueExpr(pstate, "JSON_ARRAYAGG()",
agg->arg,
- JS_FORMAT_DEFAULT);
+ JS_FORMAT_DEFAULT, InvalidOid);
returning = transformJsonConstructorOutput(pstate, agg->constructor->output,
list_make1(arg));
@@ -3882,7 +3919,8 @@ transformJsonArrayConstructor(ParseState *pstate, JsonArrayConstructor *ctor)
JsonValueExpr *jsval = castNode(JsonValueExpr, lfirst(lc));
Node *val = transformJsonValueExpr(pstate, "JSON_ARRAY()",
jsval,
- JS_FORMAT_DEFAULT);
+ JS_FORMAT_DEFAULT,
+ InvalidOid);
args = lappend(args, val);
}
@@ -3963,3 +4001,160 @@ transformJsonIsPredicate(ParseState *pstate, JsonIsPredicate *pred)
return makeJsonIsPredicate(expr, NULL, pred->item_type,
pred->unique_keys, pred->location);
}
+
+/*
+ * Transform the RETURNING clause of a JSON_*() expression if there is one and
+ * create one if not.
+ */
+static JsonReturning *
+transformJsonReturning(ParseState *pstate, JsonOutput *output, const char *fname)
+{
+ JsonReturning *returning;
+
+ if (output)
+ {
+ returning = transformJsonOutput(pstate, output, false);
+
+ Assert(OidIsValid(returning->typid));
+
+ if (returning->typid != JSONOID && returning->typid != JSONBOID)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("cannot use RETURNING type %s in %s",
+ format_type_be(returning->typid), fname),
+ parser_errposition(pstate, output->typeName->location)));
+ }
+ else
+ {
+ /* Output type is JSON by default. */
+ Oid targettype = JSONOID;
+ JsonFormatType format = JS_FORMAT_JSON;
+
+ returning = makeNode(JsonReturning);
+ returning->format = makeJsonFormat(format, JS_ENC_DEFAULT, -1);
+ returning->typid = targettype;
+ returning->typmod = -1;
+ }
+
+ return returning;
+}
+
+/*
+ * Transform a JSON() expression.
+ *
+ * JSON() is transformed into a JsonConstructorExpr of type JSCTOR_JSON_PARSE,
+ * which validates the input expression value as JSON.
+ */
+static Node *
+transformJsonParseExpr(ParseState *pstate, JsonParseExpr *jsexpr)
+{
+ JsonOutput *output = jsexpr->output;
+ JsonReturning *returning;
+ Node *arg;
+
+ returning = transformJsonReturning(pstate, output, "JSON()");
+
+ if (jsexpr->unique_keys)
+ {
+ /*
+ * Coerce string argument to text and then to json[b] in the executor
+ * node with key uniqueness check.
+ */
+ JsonValueExpr *jve = jsexpr->expr;
+ Oid arg_type;
+
+ arg = transformJsonParseArg(pstate, (Node *) jve->raw_expr, jve->format,
+ &arg_type);
+
+ if (arg_type != TEXTOID)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("cannot use non-string types with WITH UNIQUE KEYS clause"),
+ parser_errposition(pstate, jsexpr->location)));
+ }
+ else
+ {
+ /*
+ * Coerce argument to target type using CAST for compatibility with PG
+ * function-like CASTs.
+ */
+ arg = transformJsonValueExpr(pstate, "JSON()", jsexpr->expr,
+ JS_FORMAT_JSON, returning->typid);
+ }
+
+ return makeJsonConstructorExpr(pstate, JSCTOR_JSON_PARSE, list_make1(arg), NULL,
+ returning, jsexpr->unique_keys, false,
+ jsexpr->location);
+}
+
+/*
+ * Transform a JSON_SCALAR() expression.
+ *
+ * JSON_SCALAR() is transformed into a JsonConstructorExpr of type
+ * JSCTOR_JSON_SCALAR, which converts the input SQL scalar value into
+ * a json[b] value.
+ */
+static Node *
+transformJsonScalarExpr(ParseState *pstate, JsonScalarExpr *jsexpr)
+{
+ Node *arg = transformExprRecurse(pstate, (Node *) jsexpr->expr);
+ JsonOutput *output = jsexpr->output;
+ JsonReturning *returning;
+
+ returning = transformJsonReturning(pstate, output, "JSON_SCALAR()");
+
+ if (exprType(arg) == UNKNOWNOID)
+ arg = coerce_to_specific_type(pstate, arg, TEXTOID, "JSON_SCALAR");
+
+ return makeJsonConstructorExpr(pstate, JSCTOR_JSON_SCALAR, list_make1(arg), NULL,
+ returning, false, false, jsexpr->location);
+}
+
+/*
+ * Transform a JSON_SERIALIZE() expression.
+ *
+ * JSON_SERIALIZE() is transformed into a JsonConstructorExpr of type
+ * JSCTOR_JSON_SERIALIZE which converts the input JSON value into a character
+ * or bytea string.
+ */
+static Node *
+transformJsonSerializeExpr(ParseState *pstate, JsonSerializeExpr *expr)
+{
+ JsonReturning *returning;
+ Node *arg = transformJsonValueExpr(pstate, "JSON_SERIALIZE()",
+ expr->expr,
+ JS_FORMAT_JSON,
+ InvalidOid);
+
+ if (expr->output)
+ {
+ returning = transformJsonOutput(pstate, expr->output, true);
+
+ if (returning->typid != BYTEAOID)
+ {
+ char typcategory;
+ bool typispreferred;
+
+ get_type_category_preferred(returning->typid, &typcategory,
+ &typispreferred);
+ if (typcategory != TYPCATEGORY_STRING)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("cannot use RETURNING type %s in %s",
+ format_type_be(returning->typid),
+ "JSON_SERIALIZE()"),
+ errhint("Try returning a string type or bytea.")));
+ }
+ }
+ else
+ {
+ /* RETURNING TEXT FORMAT JSON is by default */
+ returning = makeNode(JsonReturning);
+ returning->format = makeJsonFormat(JS_FORMAT_JSON, JS_ENC_DEFAULT, -1);
+ returning->typid = TEXTOID;
+ returning->typmod = -1;
+ }
+
+ return makeJsonConstructorExpr(pstate, JSCTOR_JSON_SERIALIZE, list_make1(arg),
+ NULL, returning, false, false, expr->location);
+}