This patch is extracted from a larger patch that allowed setting the
default returned value from these functions to json or jsonb. That had
problems, but this piece of it is fine. For these functions only json or
jsonb can be specified in the RETURNING clause.
Extracted from an original patch from Nikita Glukhov
Reviewers have included (in no particular order) Andres Freund, Alexander
Korotkov, Pavel Stehule, Andrew Alsup, Erik Rijkers, Zihong Yu,
Himanshu Upadhyaya, Daniel Gustafsson, Justin Pryzby.
Discussion: https://postgr.es/m/
cd0bb935-0158-78a7-08b5-
904886deac4b@postgrespro.ru
JsonParseExpr *newnode = makeNode(JsonParseExpr);
COPY_NODE_FIELD(expr);
+ COPY_NODE_FIELD(output);
COPY_SCALAR_FIELD(unique_keys);
COPY_LOCATION_FIELD(location);
JsonScalarExpr *newnode = makeNode(JsonScalarExpr);
COPY_NODE_FIELD(expr);
+ COPY_NODE_FIELD(output);
COPY_LOCATION_FIELD(location);
return newnode;
_equalJsonParseExpr(const JsonParseExpr *a, const JsonParseExpr *b)
{
COMPARE_NODE_FIELD(expr);
+ COMPARE_NODE_FIELD(output);
COMPARE_SCALAR_FIELD(unique_keys);
COMPARE_LOCATION_FIELD(location);
_equalJsonScalarExpr(const JsonScalarExpr *a, const JsonScalarExpr *b)
{
COMPARE_NODE_FIELD(expr);
+ COMPARE_NODE_FIELD(output);
COMPARE_LOCATION_FIELD(location);
return true;
}
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;
;
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;
}
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)
{
/*
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");
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);
}
{
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;
{
NodeTag type;
Expr *expr; /* scalar expression */
+ JsonOutput *output; /* RETURNING clause, if specified */
int location; /* token location, or -1 if unknown */
} JsonScalarExpr;
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 ")"
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 ")"
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();
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();