diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 56505557bf..11c016495e 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -2354,6 +2354,7 @@ _copyJsonParseExpr(const JsonParseExpr *from) JsonParseExpr *newnode = makeNode(JsonParseExpr); COPY_NODE_FIELD(expr); + COPY_NODE_FIELD(output); COPY_SCALAR_FIELD(unique_keys); COPY_LOCATION_FIELD(location); @@ -2369,6 +2370,7 @@ _copyJsonScalarExpr(const JsonScalarExpr *from) JsonScalarExpr *newnode = makeNode(JsonScalarExpr); COPY_NODE_FIELD(expr); + COPY_NODE_FIELD(output); COPY_LOCATION_FIELD(location); return newnode; diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index 9ea3c5abf2..722dbe6a0d 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -875,6 +875,7 @@ static bool _equalJsonParseExpr(const JsonParseExpr *a, const JsonParseExpr *b) { COMPARE_NODE_FIELD(expr); + COMPARE_NODE_FIELD(output); COMPARE_SCALAR_FIELD(unique_keys); COMPARE_LOCATION_FIELD(location); @@ -885,6 +886,7 @@ static bool _equalJsonScalarExpr(const JsonScalarExpr *a, const JsonScalarExpr *b) { COMPARE_NODE_FIELD(expr); + COMPARE_NODE_FIELD(output); COMPARE_LOCATION_FIELD(location); return true; diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c index 4789ba6911..a094317bfc 100644 --- a/src/backend/nodes/nodeFuncs.c +++ b/src/backend/nodes/nodeFuncs.c @@ -4364,9 +4364,25 @@ raw_expression_tree_walker(Node *node, } break; case T_JsonParseExpr: - return walker(((JsonParseExpr *) node)->expr, context); + { + JsonParseExpr *jpe = (JsonParseExpr *) node; + + if (walker(jpe->expr, context)) + return true; + if (walker(jpe->output, context)) + return true; + } + break; case T_JsonScalarExpr: - return walker(((JsonScalarExpr *) node)->expr, context); + { + JsonScalarExpr *jse = (JsonScalarExpr *) node; + + if (walker(jse->expr, context)) + return true; + if (walker(jse->output, context)) + return true; + } + break; case T_JsonSerializeExpr: { JsonSerializeExpr *jse = (JsonSerializeExpr *) node; diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index eefcf90187..e5a3c528aa 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -15614,21 +15614,24 @@ json_func_expr: ; json_parse_expr: - JSON '(' json_value_expr json_key_uniqueness_constraint_opt ')' + JSON '(' json_value_expr json_key_uniqueness_constraint_opt + json_returning_clause_opt ')' { JsonParseExpr *n = makeNode(JsonParseExpr); n->expr = (JsonValueExpr *) $3; n->unique_keys = $4; + n->output = (JsonOutput *) $5; n->location = @1; $$ = (Node *) n; } ; json_scalar_expr: - JSON_SCALAR '(' a_expr ')' + JSON_SCALAR '(' a_expr json_returning_clause_opt ')' { JsonScalarExpr *n = makeNode(JsonScalarExpr); n->expr = (Expr *) $3; + n->output = (JsonOutput *) $4; n->location = @1; $$ = (Node *) n; } diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c index e140007250..31f0c9f693 100644 --- a/src/backend/parser/parse_expr.c +++ b/src/backend/parser/parse_expr.c @@ -4450,19 +4450,48 @@ transformJsonFuncExpr(ParseState *pstate, JsonFuncExpr *func) return (Node *) jsexpr; } +static JsonReturning * +transformJsonConstructorRet(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 + { + 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. */ static Node * transformJsonParseExpr(ParseState *pstate, JsonParseExpr *jsexpr) { - JsonReturning *returning = makeNode(JsonReturning); + JsonReturning *returning = transformJsonConstructorRet(pstate, jsexpr->output, + "JSON()"); Node *arg; - returning->format = makeJsonFormat(JS_FORMAT_JSON, JS_ENC_DEFAULT, -1); - returning->typid = JSONOID; - returning->typmod = -1; - if (jsexpr->unique_keys) { /* @@ -4502,12 +4531,9 @@ transformJsonParseExpr(ParseState *pstate, JsonParseExpr *jsexpr) static Node * transformJsonScalarExpr(ParseState *pstate, JsonScalarExpr *jsexpr) { - JsonReturning *returning = makeNode(JsonReturning); Node *arg = transformExprRecurse(pstate, (Node *) jsexpr->expr); - - returning->format = makeJsonFormat(JS_FORMAT_JSON, JS_ENC_DEFAULT, -1); - returning->typid = JSONOID; - returning->typmod = -1; + JsonReturning *returning = transformJsonConstructorRet(pstate, jsexpr->output, + "JSON_SCALAR()"); if (exprType(arg) == UNKNOWNOID) arg = coerce_to_specific_type(pstate, arg, TEXTOID, "JSON_SCALAR"); diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 010d5a7a75..4458d2ff90 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -10092,8 +10092,9 @@ get_json_constructor_options(JsonConstructorExpr *ctor, StringInfo buf) if (ctor->unique) appendStringInfoString(buf, " WITH UNIQUE KEYS"); - if (ctor->type != JSCTOR_JSON_PARSE && - ctor->type != JSCTOR_JSON_SCALAR) + if (!((ctor->type == JSCTOR_JSON_PARSE || + ctor->type == JSCTOR_JSON_SCALAR) && + ctor->returning->typid == JSONOID)) get_json_returning(ctor->returning, buf, true); } diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index c24fc26da1..8a9ccf6221 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -1684,6 +1684,7 @@ typedef struct JsonParseExpr { NodeTag type; JsonValueExpr *expr; /* string expression */ + JsonOutput *output; /* RETURNING clause, if specified */ bool unique_keys; /* WITH UNIQUE KEYS? */ int location; /* token location, or -1 if unknown */ } JsonParseExpr; @@ -1696,6 +1697,7 @@ typedef struct JsonScalarExpr { NodeTag type; Expr *expr; /* scalar expression */ + JsonOutput *output; /* RETURNING clause, if specified */ int location; /* token location, or -1 if unknown */ } JsonScalarExpr; diff --git a/src/test/regress/expected/sqljson.out b/src/test/regress/expected/sqljson.out index 11f5eb2d2c..6cadd87868 100644 --- a/src/test/regress/expected/sqljson.out +++ b/src/test/regress/expected/sqljson.out @@ -113,6 +113,49 @@ EXPLAIN (VERBOSE, COSTS OFF) SELECT JSON('123' WITHOUT UNIQUE KEYS); Output: JSON('123'::json) (2 rows) +SELECT JSON('123' RETURNING text); +ERROR: cannot use RETURNING type text in JSON() +LINE 1: SELECT JSON('123' RETURNING text); + ^ +EXPLAIN (VERBOSE, COSTS OFF) SELECT JSON('123'); + QUERY PLAN +----------------------------- + Result + Output: JSON('123'::json) +(2 rows) + +EXPLAIN (VERBOSE, COSTS OFF) SELECT JSON('123' RETURNING json); + QUERY PLAN +----------------------------- + Result + Output: JSON('123'::json) +(2 rows) + +EXPLAIN (VERBOSE, COSTS OFF) SELECT JSON('123' RETURNING jsonb); + QUERY PLAN +---------------------------------------------- + Result + Output: JSON('123'::jsonb RETURNING jsonb) +(2 rows) + +SELECT pg_typeof(JSON('123')); + pg_typeof +----------- + json +(1 row) + +SELECT pg_typeof(JSON('123' RETURNING json)); + pg_typeof +----------- + json +(1 row) + +SELECT pg_typeof(JSON('123' RETURNING jsonb)); + pg_typeof +----------- + jsonb +(1 row) + -- JSON_SCALAR() SELECT JSON_SCALAR(); ERROR: syntax error at or near ")" @@ -204,6 +247,20 @@ EXPLAIN (VERBOSE, COSTS OFF) SELECT JSON_SCALAR('123'); Output: JSON_SCALAR('123'::text) (2 rows) +EXPLAIN (VERBOSE, COSTS OFF) SELECT JSON_SCALAR(123 RETURNING json); + QUERY PLAN +---------------------------- + Result + Output: JSON_SCALAR(123) +(2 rows) + +EXPLAIN (VERBOSE, COSTS OFF) SELECT JSON_SCALAR(123 RETURNING jsonb); + QUERY PLAN +-------------------------------------------- + Result + Output: JSON_SCALAR(123 RETURNING jsonb) +(2 rows) + -- JSON_SERIALIZE() SELECT JSON_SERIALIZE(); ERROR: syntax error at or near ")" diff --git a/src/test/regress/sql/sqljson.sql b/src/test/regress/sql/sqljson.sql index 98bd93c110..51fc659b58 100644 --- a/src/test/regress/sql/sqljson.sql +++ b/src/test/regress/sql/sqljson.sql @@ -23,6 +23,14 @@ EXPLAIN (VERBOSE, COSTS OFF) SELECT JSON('123'::bytea FORMAT JSON ENCODING UTF8) EXPLAIN (VERBOSE, COSTS OFF) SELECT JSON('123' WITH UNIQUE KEYS); EXPLAIN (VERBOSE, COSTS OFF) SELECT JSON('123' WITHOUT UNIQUE KEYS); +SELECT JSON('123' RETURNING text); + +EXPLAIN (VERBOSE, COSTS OFF) SELECT JSON('123'); +EXPLAIN (VERBOSE, COSTS OFF) SELECT JSON('123' RETURNING json); +EXPLAIN (VERBOSE, COSTS OFF) SELECT JSON('123' RETURNING jsonb); +SELECT pg_typeof(JSON('123')); +SELECT pg_typeof(JSON('123' RETURNING json)); +SELECT pg_typeof(JSON('123' RETURNING jsonb)); -- JSON_SCALAR() SELECT JSON_SCALAR(); @@ -41,6 +49,8 @@ SELECT JSON_SCALAR('{}'::jsonb); EXPLAIN (VERBOSE, COSTS OFF) SELECT JSON_SCALAR(123); EXPLAIN (VERBOSE, COSTS OFF) SELECT JSON_SCALAR('123'); +EXPLAIN (VERBOSE, COSTS OFF) SELECT JSON_SCALAR(123 RETURNING json); +EXPLAIN (VERBOSE, COSTS OFF) SELECT JSON_SCALAR(123 RETURNING jsonb); -- JSON_SERIALIZE() SELECT JSON_SERIALIZE();