aboutsummaryrefslogtreecommitdiff
path: root/src/backend
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend')
-rw-r--r--src/backend/executor/execExpr.c30
-rw-r--r--src/backend/executor/execExprInterp.c41
-rw-r--r--src/backend/nodes/nodeFuncs.c30
-rw-r--r--src/backend/parser/gram.y49
-rw-r--r--src/backend/parser/parse_expr.c225
-rw-r--r--src/backend/parser/parse_target.c12
-rw-r--r--src/backend/utils/adt/format_type.c4
-rw-r--r--src/backend/utils/adt/jsonb.c15
-rw-r--r--src/backend/utils/adt/ruleutils.c16
9 files changed, 398 insertions, 24 deletions
diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c
index bf3a08c5f08..2c62b0c9c84 100644
--- a/src/backend/executor/execExpr.c
+++ b/src/backend/executor/execExpr.c
@@ -48,6 +48,7 @@
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/datum.h"
+#include "utils/jsonfuncs.h"
#include "utils/lsyscache.h"
#include "utils/typcache.h"
@@ -2311,6 +2312,12 @@ ExecInitExprRec(Expr *node, ExprState *state,
{
ExecInitExprRec(ctor->func, state, resv, resnull);
}
+ else if ((ctor->type == JSCTOR_JSON_PARSE && !ctor->unique) ||
+ ctor->type == JSCTOR_JSON_SERIALIZE)
+ {
+ /* Use the value of the first argument as result */
+ ExecInitExprRec(linitial(args), state, resv, resnull);
+ }
else
{
JsonConstructorExprState *jcstate;
@@ -2349,6 +2356,29 @@ ExecInitExprRec(Expr *node, ExprState *state,
argno++;
}
+ /* prepare type cache for datum_to_json[b]() */
+ if (ctor->type == JSCTOR_JSON_SCALAR)
+ {
+ bool is_jsonb =
+ ctor->returning->format->format_type == JS_FORMAT_JSONB;
+
+ jcstate->arg_type_cache =
+ palloc(sizeof(*jcstate->arg_type_cache) * nargs);
+
+ for (int i = 0; i < nargs; i++)
+ {
+ JsonTypeCategory category;
+ Oid outfuncid;
+ Oid typid = jcstate->arg_types[i];
+
+ json_categorize_type(typid, is_jsonb,
+ &category, &outfuncid);
+
+ jcstate->arg_type_cache[i].outfuncid = outfuncid;
+ jcstate->arg_type_cache[i].category = (int) category;
+ }
+ }
+
ExprEvalPushStep(state, &scratch);
}
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index 851946a9272..24c2b60c62a 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -4002,6 +4002,47 @@ ExecEvalJsonConstructor(ExprState *state, ExprEvalStep *op,
jcstate->arg_types,
jcstate->constructor->absent_on_null,
jcstate->constructor->unique);
+ else if (ctor->type == JSCTOR_JSON_SCALAR)
+ {
+ if (jcstate->arg_nulls[0])
+ {
+ res = (Datum) 0;
+ isnull = true;
+ }
+ else
+ {
+ Datum value = jcstate->arg_values[0];
+ Oid outfuncid = jcstate->arg_type_cache[0].outfuncid;
+ JsonTypeCategory category = (JsonTypeCategory)
+ jcstate->arg_type_cache[0].category;
+
+ if (is_jsonb)
+ res = datum_to_jsonb(value, category, outfuncid);
+ else
+ res = datum_to_json(value, category, outfuncid);
+ }
+ }
+ else if (ctor->type == JSCTOR_JSON_PARSE)
+ {
+ if (jcstate->arg_nulls[0])
+ {
+ res = (Datum) 0;
+ isnull = true;
+ }
+ else
+ {
+ Datum value = jcstate->arg_values[0];
+ text *js = DatumGetTextP(value);
+
+ if (is_jsonb)
+ res = jsonb_from_text(js, true);
+ else
+ {
+ (void) json_validate(js, true, true);
+ res = value;
+ }
+ }
+ }
else
elog(ERROR, "invalid JsonConstructorExpr type %d", ctor->type);
diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c
index 503d76aae07..c03f4f23e26 100644
--- a/src/backend/nodes/nodeFuncs.c
+++ b/src/backend/nodes/nodeFuncs.c
@@ -3899,6 +3899,36 @@ raw_expression_tree_walker_impl(Node *node,
return true;
}
break;
+ case T_JsonParseExpr:
+ {
+ JsonParseExpr *jpe = (JsonParseExpr *) node;
+
+ if (WALK(jpe->expr))
+ return true;
+ if (WALK(jpe->output))
+ return true;
+ }
+ break;
+ case T_JsonScalarExpr:
+ {
+ JsonScalarExpr *jse = (JsonScalarExpr *) node;
+
+ if (WALK(jse->expr))
+ return true;
+ if (WALK(jse->output))
+ return true;
+ }
+ break;
+ case T_JsonSerializeExpr:
+ {
+ JsonSerializeExpr *jse = (JsonSerializeExpr *) node;
+
+ if (WALK(jse->expr))
+ return true;
+ if (WALK(jse->output))
+ return true;
+ }
+ break;
case T_JsonConstructorExpr:
{
JsonConstructorExpr *ctor = (JsonConstructorExpr *) node;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index e7134add118..856d5dee0e7 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -566,7 +566,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
%type <list> copy_options
%type <typnam> Typename SimpleTypename ConstTypename
- GenericType Numeric opt_float
+ GenericType Numeric opt_float JsonType
Character ConstCharacter
CharacterWithLength CharacterWithoutLength
ConstDatetime ConstInterval
@@ -723,6 +723,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
INTERSECT INTERVAL INTO INVOKER IS ISNULL ISOLATION
JOIN JSON JSON_ARRAY JSON_ARRAYAGG JSON_OBJECT JSON_OBJECTAGG
+ JSON_SCALAR JSON_SERIALIZE
KEY KEYS
@@ -13990,6 +13991,7 @@ SimpleTypename:
$$->typmods = list_make2(makeIntConst(INTERVAL_FULL_RANGE, -1),
makeIntConst($3, @3));
}
+ | JsonType { $$ = $1; }
;
/* We have a separate ConstTypename to allow defaulting fixed-length
@@ -14008,6 +14010,7 @@ ConstTypename:
| ConstBit { $$ = $1; }
| ConstCharacter { $$ = $1; }
| ConstDatetime { $$ = $1; }
+ | JsonType { $$ = $1; }
;
/*
@@ -14376,6 +14379,13 @@ interval_second:
}
;
+JsonType:
+ JSON
+ {
+ $$ = SystemTypeName("json");
+ $$->location = @1;
+ }
+ ;
/*****************************************************************************
*
@@ -15634,7 +15644,36 @@ func_expr_common_subexpr:
n->location = @1;
$$ = (Node *) n;
}
- ;
+ | JSON '(' json_value_expr json_key_uniqueness_constraint_opt ')'
+ {
+ JsonParseExpr *n = makeNode(JsonParseExpr);
+
+ n->expr = (JsonValueExpr *) $3;
+ n->unique_keys = $4;
+ n->output = NULL;
+ n->location = @1;
+ $$ = (Node *) n;
+ }
+ | JSON_SCALAR '(' a_expr ')'
+ {
+ JsonScalarExpr *n = makeNode(JsonScalarExpr);
+
+ n->expr = (Expr *) $3;
+ n->output = NULL;
+ n->location = @1;
+ $$ = (Node *) n;
+ }
+ | JSON_SERIALIZE '(' json_value_expr json_returning_clause_opt ')'
+ {
+ JsonSerializeExpr *n = makeNode(JsonSerializeExpr);
+
+ n->expr = (JsonValueExpr *) $3;
+ n->output = (JsonOutput *) $4;
+ n->location = @1;
+ $$ = (Node *) n;
+ }
+ ;
+
/*
* SQL/XML support
@@ -17075,7 +17114,6 @@ unreserved_keyword:
| INSTEAD
| INVOKER
| ISOLATION
- | JSON
| KEY
| KEYS
| LABEL
@@ -17290,10 +17328,13 @@ col_name_keyword:
| INT_P
| INTEGER
| INTERVAL
+ | JSON
| JSON_ARRAY
| JSON_ARRAYAGG
| JSON_OBJECT
| JSON_OBJECTAGG
+ | JSON_SCALAR
+ | JSON_SERIALIZE
| LEAST
| NATIONAL
| NCHAR
@@ -17654,6 +17695,8 @@ bare_label_keyword:
| JSON_ARRAYAGG
| JSON_OBJECT
| JSON_OBJECTAGG
+ | JSON_SCALAR
+ | JSON_SERIALIZE
| KEY
| KEYS
| LABEL
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);
+}
diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c
index 4cca97ff9c1..57247de363b 100644
--- a/src/backend/parser/parse_target.c
+++ b/src/backend/parser/parse_target.c
@@ -1953,6 +1953,18 @@ FigureColnameInternal(Node *node, char **name)
/* make XMLSERIALIZE act like a regular function */
*name = "xmlserialize";
return 2;
+ case T_JsonParseExpr:
+ /* make JSON act like a regular function */
+ *name = "json";
+ return 2;
+ case T_JsonScalarExpr:
+ /* make JSON_SCALAR act like a regular function */
+ *name = "json_scalar";
+ return 2;
+ case T_JsonSerializeExpr:
+ /* make JSON_SERIALIZE act like a regular function */
+ *name = "json_serialize";
+ return 2;
case T_JsonObjectConstructor:
/* make JSON_OBJECT act like a regular function */
*name = "json_object";
diff --git a/src/backend/utils/adt/format_type.c b/src/backend/utils/adt/format_type.c
index 12402a06379..36c45a39780 100644
--- a/src/backend/utils/adt/format_type.c
+++ b/src/backend/utils/adt/format_type.c
@@ -294,6 +294,10 @@ format_type_extended(Oid type_oid, int32 typemod, bits16 flags)
else
buf = pstrdup("character varying");
break;
+
+ case JSONOID:
+ buf = pstrdup("json");
+ break;
}
if (buf == NULL)
diff --git a/src/backend/utils/adt/jsonb.c b/src/backend/utils/adt/jsonb.c
index 5ea582a8884..9781852b0cb 100644
--- a/src/backend/utils/adt/jsonb.c
+++ b/src/backend/utils/adt/jsonb.c
@@ -33,6 +33,7 @@ typedef struct JsonbInState
{
JsonbParseState *parseState;
JsonbValue *res;
+ bool unique_keys;
Node *escontext;
} JsonbInState;
@@ -45,7 +46,8 @@ typedef struct JsonbAggState
Oid val_output_func;
} JsonbAggState;
-static inline Datum jsonb_from_cstring(char *json, int len, Node *escontext);
+static inline Datum jsonb_from_cstring(char *json, int len, bool unique_keys,
+ Node *escontext);
static bool checkStringLen(size_t len, Node *escontext);
static JsonParseErrorType jsonb_in_object_start(void *pstate);
static JsonParseErrorType jsonb_in_object_end(void *pstate);
@@ -76,7 +78,7 @@ jsonb_in(PG_FUNCTION_ARGS)
{
char *json = PG_GETARG_CSTRING(0);
- return jsonb_from_cstring(json, strlen(json), fcinfo->context);
+ return jsonb_from_cstring(json, strlen(json), false, fcinfo->context);
}
/*
@@ -100,7 +102,7 @@ jsonb_recv(PG_FUNCTION_ARGS)
else
elog(ERROR, "unsupported jsonb version number %d", version);
- return jsonb_from_cstring(str, nbytes, NULL);
+ return jsonb_from_cstring(str, nbytes, false, NULL);
}
/*
@@ -147,10 +149,11 @@ jsonb_send(PG_FUNCTION_ARGS)
* Turns json text string into a jsonb Datum.
*/
Datum
-jsonb_from_text(text *js)
+jsonb_from_text(text *js, bool unique_keys)
{
return jsonb_from_cstring(VARDATA_ANY(js),
VARSIZE_ANY_EXHDR(js),
+ unique_keys,
NULL);
}
@@ -247,7 +250,7 @@ jsonb_typeof(PG_FUNCTION_ARGS)
* instead of being thrown.
*/
static inline Datum
-jsonb_from_cstring(char *json, int len, Node *escontext)
+jsonb_from_cstring(char *json, int len, bool unique_keys, Node *escontext)
{
JsonLexContext *lex;
JsonbInState state;
@@ -257,6 +260,7 @@ jsonb_from_cstring(char *json, int len, Node *escontext)
memset(&sem, 0, sizeof(sem));
lex = makeJsonLexContextCstringLen(json, len, GetDatabaseEncoding(), true);
+ state.unique_keys = unique_keys;
state.escontext = escontext;
sem.semstate = (void *) &state;
@@ -293,6 +297,7 @@ jsonb_in_object_start(void *pstate)
JsonbInState *_state = (JsonbInState *) pstate;
_state->res = pushJsonbValue(&_state->parseState, WJB_BEGIN_OBJECT, NULL);
+ _state->parseState->unique_keys = _state->unique_keys;
return JSON_SUCCESS;
}
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index fcb2f45f623..03f2835c3f1 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -10832,6 +10832,15 @@ get_json_constructor(JsonConstructorExpr *ctor, deparse_context *context,
case JSCTOR_JSON_ARRAY:
funcname = "JSON_ARRAY";
break;
+ case JSCTOR_JSON_PARSE:
+ funcname = "JSON";
+ break;
+ case JSCTOR_JSON_SCALAR:
+ funcname = "JSON_SCALAR";
+ break;
+ case JSCTOR_JSON_SERIALIZE:
+ funcname = "JSON_SERIALIZE";
+ break;
default:
elog(ERROR, "invalid JsonConstructorType %d", ctor->type);
}
@@ -10879,7 +10888,12 @@ get_json_constructor_options(JsonConstructorExpr *ctor, StringInfo buf)
if (ctor->unique)
appendStringInfoString(buf, " WITH UNIQUE KEYS");
- get_json_returning(ctor->returning, buf, true);
+ /*
+ * Append RETURNING clause if needed; JSON() and JSON_SCALAR() don't
+ * support one.
+ */
+ if (ctor->type != JSCTOR_JSON_PARSE && ctor->type != JSCTOR_JSON_SCALAR)
+ get_json_returning(ctor->returning, buf, true);
}
/*