diff options
Diffstat (limited to 'src/backend/parser/parse_expr.c')
-rw-r--r-- | src/backend/parser/parse_expr.c | 225 |
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); +} |