<returnvalue>{"a": "1", "b": "2"}</returnvalue>
</para></entry>
</row>
+ <row>
+ <entry role="func_table_entry">
+ <para role="func_signature">
+ <indexterm><primary>json constructor</primary></indexterm>
+ <function>json</function> (
+ <replaceable>expression</replaceable>
+ <optional> <literal>FORMAT JSON</literal> <optional> <literal>ENCODING UTF8</literal> </optional></optional>
+ <optional> { <literal>WITH</literal> | <literal>WITHOUT</literal> } <literal>UNIQUE</literal> <optional> <literal>KEYS</literal> </optional></optional>
+ </para>
+ <para>
+ Converts a given expression specified as <type>text</type> or
+ <type>bytea</type> string (in UTF8 encoding) into a JSON
+ value. If <replaceable>expression</replaceable> is NULL, an
+ <acronym>SQL</acronym> null value is returned.
+ If <literal>WITH UNIQUE</literal> is specified, the
+ <replaceable>expression</replaceable> must not contain any duplicate
+ object keys.
+ </para>
+ <para>
+ <literal>json('{"a":123, "b":[true,"foo"], "a":"bar"}')</literal>
+ <returnvalue>{"a":123, "b":[true,"foo"], "a":"bar"}</returnvalue>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry role="func_table_entry">
+ <para role="func_signature">
+ <indexterm><primary>json_scalar</primary></indexterm>
+ <function>json_scalar</function> (<replaceable>expression</replaceable>)
+ </para>
+ <para>
+ Converts a given SQL scalar value into a JSON scalar value.
+ If the input is NULL, an <acronym>SQL</acronym> null is returned. If
+ the input is number or a boolean value, a corresponding JSON number
+ or boolean value is returned. For any other value, a JSON string is
+ returned.
+ </para>
+ <para>
+ <literal>json_scalar(123.45)</literal>
+ <returnvalue>123.45</returnvalue>
+ </para>
+ <para>
+ <literal>json_scalar(CURRENT_TIMESTAMP)</literal>
+ <returnvalue>"2022-05-10T10:51:04.62128-04:00"</returnvalue>
+ </para></entry>
+ </row>
+ <row>
+ <entry role="func_table_entry">
+ <para role="func_signature">
+ <function>json_serialize</function> (
+ <replaceable>expression</replaceable> <optional> <literal>FORMAT JSON</literal> <optional> <literal>ENCODING UTF8</literal> </optional> </optional>
+ <optional> <literal>RETURNING</literal> <replaceable>data_type</replaceable> <optional> <literal>FORMAT JSON</literal> <optional> <literal>ENCODING UTF8</literal> </optional> </optional> </optional>)
+ </para>
+ <para>
+ Converts an SQL/JSON expression into a character or binary string. The
+ <replaceable>expression</replaceable> can be of any JSON type, any
+ character string type, or <type>bytea</type> in UTF8 encoding.
+ The returned type used in <literal> RETURNING</literal> can be any
+ character string type or <type>bytea</type>. The default is
+ <type>text</type>.
+ </para>
+ <para>
+ <literal>json_serialize('{ "a" : 1 } ' RETURNING bytea)</literal>
+ <returnvalue>\x7b20226122203a2031207d20</returnvalue>
+ </para></entry>
+ </row>
</tbody>
</tgroup>
</table>
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/datum.h"
+#include "utils/jsonfuncs.h"
#include "utils/lsyscache.h"
#include "utils/typcache.h"
{
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;
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);
}
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);
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;
%type <list> copy_options
%type <typnam> Typename SimpleTypename ConstTypename
- GenericType Numeric opt_float
+ GenericType Numeric opt_float JsonType
Character ConstCharacter
CharacterWithLength CharacterWithoutLength
ConstDatetime ConstInterval
INTERSECT INTERVAL INTO INVOKER IS ISNULL ISOLATION
JOIN JSON JSON_ARRAY JSON_ARRAYAGG JSON_OBJECT JSON_OBJECTAGG
+ JSON_SCALAR JSON_SERIALIZE
KEY KEYS
$$->typmods = list_make2(makeIntConst(INTERVAL_FULL_RANGE, -1),
makeIntConst($3, @3));
}
+ | JsonType { $$ = $1; }
;
/* We have a separate ConstTypename to allow defaulting fixed-length
| ConstBit { $$ = $1; }
| ConstCharacter { $$ = $1; }
| ConstDatetime { $$ = $1; }
+ | JsonType { $$ = $1; }
;
/*
}
;
+JsonType:
+ JSON
+ {
+ $$ = SystemTypeName("json");
+ $$->location = @1;
+ }
+ ;
/*****************************************************************************
*
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
| INSTEAD
| INVOKER
| ISOLATION
- | JSON
| KEY
| KEYS
| LABEL
| INT_P
| INTEGER
| INTERVAL
+ | JSON
| JSON_ARRAY
| JSON_ARRAYAGG
| JSON_OBJECT
| JSON_OBJECTAGG
+ | JSON_SCALAR
+ | JSON_SERIALIZE
| LEAST
| NATIONAL
| NCHAR
| JSON_ARRAYAGG
| JSON_OBJECT
| JSON_OBJECTAGG
+ | JSON_SCALAR
+ | JSON_SERIALIZE
| KEY
| KEYS
| LABEL
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,
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));
/*
* 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;
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 ?
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,
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;
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);
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,
arg = transformJsonValueExpr(pstate, "JSON_ARRAYAGG()",
agg->arg,
- JS_FORMAT_DEFAULT);
+ JS_FORMAT_DEFAULT, InvalidOid);
returning = transformJsonConstructorOutput(pstate, agg->constructor->output,
list_make1(arg));
JsonValueExpr *jsval = castNode(JsonValueExpr, lfirst(lc));
Node *val = transformJsonValueExpr(pstate, "JSON_ARRAY()",
jsval,
- JS_FORMAT_DEFAULT);
+ JS_FORMAT_DEFAULT,
+ InvalidOid);
args = lappend(args, val);
}
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);
+}
/* 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";
else
buf = pstrdup("character varying");
break;
+
+ case JSONOID:
+ buf = pstrdup("json");
+ break;
}
if (buf == NULL)
{
JsonbParseState *parseState;
JsonbValue *res;
+ bool unique_keys;
Node *escontext;
} JsonbInState;
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);
{
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);
}
/*
else
elog(ERROR, "unsupported jsonb version number %d", version);
- return jsonb_from_cstring(str, nbytes, NULL);
+ return jsonb_from_cstring(str, nbytes, false, NULL);
}
/*
* 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);
}
* 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;
memset(&sem, 0, sizeof(sem));
lex = makeJsonLexContextCstringLen(json, len, GetDatabaseEncoding(), true);
+ state.unique_keys = unique_keys;
state.escontext = escontext;
sem.semstate = (void *) &state;
JsonbInState *_state = (JsonbInState *) pstate;
_state->res = pushJsonbValue(&_state->parseState, WJB_BEGIN_OBJECT, NULL);
+ _state->parseState->unique_keys = _state->unique_keys;
return JSON_SUCCESS;
}
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);
}
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);
}
/*
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 202307111
+#define CATALOG_VERSION_NO 202307261
#endif
JsonValueExpr *value; /* JSON value expression */
} JsonKeyValue;
+/*
+ * JsonParseExpr -
+ * untransformed representation of JSON()
+ */
+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;
+
+/*
+ * JsonScalarExpr -
+ * untransformed representation of JSON_SCALAR()
+ */
+typedef struct JsonScalarExpr
+{
+ NodeTag type;
+ Expr *expr; /* scalar expression */
+ JsonOutput *output; /* RETURNING clause, if specified */
+ int location; /* token location, or -1 if unknown */
+} JsonScalarExpr;
+
+/*
+ * JsonSerializeExpr -
+ * untransformed representation of JSON_SERIALIZE() function
+ */
+typedef struct JsonSerializeExpr
+{
+ NodeTag type;
+ JsonValueExpr *expr; /* json value expression */
+ JsonOutput *output; /* RETURNING clause, if specified */
+ int location; /* token location, or -1 if unknown */
+} JsonSerializeExpr;
+
/*
* JsonObjectConstructor -
* untransformed representation of JSON_OBJECT() constructor
JSCTOR_JSON_OBJECT = 1,
JSCTOR_JSON_ARRAY = 2,
JSCTOR_JSON_OBJECTAGG = 3,
- JSCTOR_JSON_ARRAYAGG = 4
+ JSCTOR_JSON_ARRAYAGG = 4,
+ JSCTOR_JSON_PARSE = 5,
+ JSCTOR_JSON_SCALAR = 6,
+ JSCTOR_JSON_SERIALIZE = 7
} JsonConstructorType;
/*
PG_KEYWORD("isnull", ISNULL, TYPE_FUNC_NAME_KEYWORD, AS_LABEL)
PG_KEYWORD("isolation", ISOLATION, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("join", JOIN, TYPE_FUNC_NAME_KEYWORD, BARE_LABEL)
-PG_KEYWORD("json", JSON, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("json", JSON, COL_NAME_KEYWORD, BARE_LABEL)
PG_KEYWORD("json_array", JSON_ARRAY, COL_NAME_KEYWORD, BARE_LABEL)
PG_KEYWORD("json_arrayagg", JSON_ARRAYAGG, COL_NAME_KEYWORD, BARE_LABEL)
PG_KEYWORD("json_object", JSON_OBJECT, COL_NAME_KEYWORD, BARE_LABEL)
PG_KEYWORD("json_objectagg", JSON_OBJECTAGG, COL_NAME_KEYWORD, BARE_LABEL)
+PG_KEYWORD("json_scalar", JSON_SCALAR, COL_NAME_KEYWORD, BARE_LABEL)
+PG_KEYWORD("json_serialize", JSON_SERIALIZE, COL_NAME_KEYWORD, BARE_LABEL)
PG_KEYWORD("key", KEY, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("keys", KEYS, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("label", LABEL, UNRESERVED_KEYWORD, BARE_LABEL)
Oid outfuncoid);
extern Datum datum_to_jsonb(Datum val, JsonTypeCategory tcategory,
Oid outfuncoid);
-extern Datum jsonb_from_text(text *js);
+extern Datum jsonb_from_text(text *js, bool unique_keys);
#endif
printf("Found json=%s\n", json);
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select json ( null )", ECPGt_EOIT,
+ ECPGt_char,(json),(long)1024,(long)1,(1024)*sizeof(char),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);
+#line 42 "sqljson.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 42 "sqljson.pgc"
+
+ printf("Found json=%s\n", json);
+
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select json ( '{ \"a\" : 1 } ' format json )", ECPGt_EOIT,
+ ECPGt_char,(json),(long)1024,(long)1,(1024)*sizeof(char),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);
+#line 45 "sqljson.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 45 "sqljson.pgc"
+
+ printf("Found json=%s\n", json);
+
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select json ( '{ \"a\" : 1 } ' format json encoding UTF8 )", ECPGt_EOIT,
+ ECPGt_char,(json),(long)1024,(long)1,(1024)*sizeof(char),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);
+#line 48 "sqljson.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 48 "sqljson.pgc"
+
+ // error
+
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select json ( ' 1 ' :: jsonb )", ECPGt_EOIT,
+ ECPGt_char,(json),(long)1024,(long)1,(1024)*sizeof(char),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);
+#line 51 "sqljson.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 51 "sqljson.pgc"
+
+ printf("Found json=%s\n", json);
+
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select json ( ' 1 ' :: json with unique keys ) into json", ECPGt_EOIT, ECPGt_EORT);
+#line 54 "sqljson.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 54 "sqljson.pgc"
+
+ // error
+
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select json ( '{\"a\": 1, \"a\": 2}' )", ECPGt_EOIT,
+ ECPGt_char,(json),(long)1024,(long)1,(1024)*sizeof(char),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);
+#line 57 "sqljson.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 57 "sqljson.pgc"
+
+ printf("Found json=%s\n", json);
+
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select json ( '{\"a\": 1, \"a\": 2}' with unique keys )", ECPGt_EOIT,
+ ECPGt_char,(json),(long)1024,(long)1,(1024)*sizeof(char),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);
+#line 60 "sqljson.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 60 "sqljson.pgc"
+
+ // error
+
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select json_scalar ( null )", ECPGt_EOIT,
+ ECPGt_char,(json),(long)1024,(long)1,(1024)*sizeof(char),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);
+#line 63 "sqljson.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 63 "sqljson.pgc"
+
+ printf("Found json=%s\n", json);
+
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select json_scalar ( null :: int )", ECPGt_EOIT,
+ ECPGt_char,(json),(long)1024,(long)1,(1024)*sizeof(char),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);
+#line 66 "sqljson.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 66 "sqljson.pgc"
+
+ printf("Found json=%s\n", json);
+
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select json_scalar ( 123.45 )", ECPGt_EOIT,
+ ECPGt_char,(json),(long)1024,(long)1,(1024)*sizeof(char),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);
+#line 69 "sqljson.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 69 "sqljson.pgc"
+
+ printf("Found json=%s\n", json);
+
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select json_scalar ( true )", ECPGt_EOIT,
+ ECPGt_char,(json),(long)1024,(long)1,(1024)*sizeof(char),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);
+#line 72 "sqljson.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 72 "sqljson.pgc"
+
+ printf("Found json=%s\n", json);
+
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select json_scalar ( ' 123.45' )", ECPGt_EOIT,
+ ECPGt_char,(json),(long)1024,(long)1,(1024)*sizeof(char),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);
+#line 75 "sqljson.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 75 "sqljson.pgc"
+
+ printf("Found json=%s\n", json);
+
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select json_scalar ( '2020-06-07 01:02:03' :: timestamp )", ECPGt_EOIT,
+ ECPGt_char,(json),(long)1024,(long)1,(1024)*sizeof(char),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);
+#line 78 "sqljson.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 78 "sqljson.pgc"
+
+ printf("Found json=%s\n", json);
+
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select json_scalar ( '{}' :: jsonb )", ECPGt_EOIT,
+ ECPGt_char,(json),(long)1024,(long)1,(1024)*sizeof(char),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);
+#line 81 "sqljson.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 81 "sqljson.pgc"
+
+ printf("Found json=%s\n", json);
+
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select json_serialize ( null )", ECPGt_EOIT,
+ ECPGt_char,(json),(long)1024,(long)1,(1024)*sizeof(char),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);
+#line 84 "sqljson.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 84 "sqljson.pgc"
+
+ printf("Found json=%s\n", json);
+
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select json_serialize ( json ( '{ \"a\" : 1 } ' ) )", ECPGt_EOIT,
+ ECPGt_char,(json),(long)1024,(long)1,(1024)*sizeof(char),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);
+#line 87 "sqljson.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 87 "sqljson.pgc"
+
+ printf("Found json=%s\n", json);
+
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select json_serialize ( '{ \"a\" : 1 } ' )", ECPGt_EOIT,
+ ECPGt_char,(json),(long)1024,(long)1,(1024)*sizeof(char),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);
+#line 90 "sqljson.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 90 "sqljson.pgc"
+
+ printf("Found json=%s\n", json);
+
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select json_serialize ( '1' format json )", ECPGt_EOIT,
+ ECPGt_char,(json),(long)1024,(long)1,(1024)*sizeof(char),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);
+#line 93 "sqljson.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 93 "sqljson.pgc"
+
+ printf("Found json=%s\n", json);
+
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select json_serialize ( '{ \"a\" : 1 } ' returning varchar )", ECPGt_EOIT,
+ ECPGt_char,(json),(long)1024,(long)1,(1024)*sizeof(char),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);
+#line 96 "sqljson.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 96 "sqljson.pgc"
+
+ printf("Found json=%s\n", json);
+
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select json_serialize ( '{ \"a\" : 1 } ' returning jsonb )", ECPGt_EOIT, ECPGt_EORT);
+#line 99 "sqljson.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 99 "sqljson.pgc"
+
+ // error
+
{ ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "with val ( js ) as ( values ( '{ \"a\": 1, \"b\": [{ \"a\": 1, \"b\": 0, \"a\": 2 }] }' ) ) select js is json \"IS JSON\" , js is not json \"IS NOT JSON\" , js is json value \"IS VALUE\" , js is json object \"IS OBJECT\" , js is json array \"IS ARRAY\" , js is json scalar \"IS SCALAR\" , js is json without unique keys \"WITHOUT UNIQUE\" , js is json with unique keys \"WITH UNIQUE\" from val", ECPGt_EOIT,
ECPGt_bool,&(is_json[0]),(long)1,(long)1,sizeof(bool),
ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L,
ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L,
ECPGt_bool,&(is_json[7]),(long)1,(long)1,sizeof(bool),
ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);
-#line 54 "sqljson.pgc"
+#line 114 "sqljson.pgc"
if (sqlca.sqlcode < 0) sqlprint();}
-#line 54 "sqljson.pgc"
+#line 114 "sqljson.pgc"
for (int i = 0; i < sizeof(is_json); i++)
printf("Found is_json[%d]: %s\n", i, is_json[i] ? "true" : "false");
{ ECPGdisconnect(__LINE__, "CURRENT");
-#line 58 "sqljson.pgc"
+#line 118 "sqljson.pgc"
if (sqlca.sqlcode < 0) sqlprint();}
-#line 58 "sqljson.pgc"
+#line 118 "sqljson.pgc"
return 0;
[NO_PID]: sqlca: code: 0, state: 00000
[NO_PID]: ecpg_get_data on line 39: RESULT: {"1": 1} offset: -1; array: no
[NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_execute on line 42: query: with val ( js ) as ( values ( '{ "a": 1, "b": [{ "a": 1, "b": 0, "a": 2 }] }' ) ) select js is json "IS JSON" , js is not json "IS NOT JSON" , js is json value "IS VALUE" , js is json object "IS OBJECT" , js is json array "IS ARRAY" , js is json scalar "IS SCALAR" , js is json without unique keys "WITHOUT UNIQUE" , js is json with unique keys "WITH UNIQUE" from val; with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: ecpg_execute on line 42: query: select json ( null ); with 0 parameter(s) on connection ecpg1_regression
[NO_PID]: sqlca: code: 0, state: 00000
[NO_PID]: ecpg_execute on line 42: using PQexec
[NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_process_output on line 42: correctly got 1 tuples with 8 fields
+[NO_PID]: ecpg_process_output on line 42: correctly got 1 tuples with 1 fields
[NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_get_data on line 42: RESULT: t offset: -1; array: no
+[NO_PID]: ecpg_get_data on line 42: RESULT: offset: -1; array: no
[NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_get_data on line 42: RESULT: f offset: -1; array: no
+[NO_PID]: raising sqlcode -213 on line 42: null value without indicator on line 42
+[NO_PID]: sqlca: code: -213, state: 22002
+SQL error: null value without indicator on line 42
+[NO_PID]: ecpg_execute on line 45: query: select json ( '{ "a" : 1 } ' format json ); with 0 parameter(s) on connection ecpg1_regression
[NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_get_data on line 42: RESULT: t offset: -1; array: no
+[NO_PID]: ecpg_execute on line 45: using PQexec
[NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_get_data on line 42: RESULT: t offset: -1; array: no
+[NO_PID]: ecpg_process_output on line 45: correctly got 1 tuples with 1 fields
[NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_get_data on line 42: RESULT: f offset: -1; array: no
+[NO_PID]: ecpg_get_data on line 45: RESULT: { "a" : 1 } offset: -1; array: no
[NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_get_data on line 42: RESULT: f offset: -1; array: no
+[NO_PID]: ecpg_execute on line 48: query: select json ( '{ "a" : 1 } ' format json encoding UTF8 ); with 0 parameter(s) on connection ecpg1_regression
[NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_get_data on line 42: RESULT: t offset: -1; array: no
+[NO_PID]: ecpg_execute on line 48: using PQexec
[NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_get_data on line 42: RESULT: f offset: -1; array: no
+[NO_PID]: ecpg_check_PQresult on line 48: bad response - ERROR: JSON ENCODING clause is only allowed for bytea input type
+LINE 1: select json ( '{ "a" : 1 } ' format json encoding UTF8 )
+ ^
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: raising sqlstate 42804 (sqlcode -400): JSON ENCODING clause is only allowed for bytea input type on line 48
+[NO_PID]: sqlca: code: -400, state: 42804
+SQL error: JSON ENCODING clause is only allowed for bytea input type on line 48
+[NO_PID]: ecpg_execute on line 51: query: select json ( ' 1 ' :: jsonb ); with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 51: using PQexec
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 51: correctly got 1 tuples with 1 fields
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_get_data on line 51: RESULT: 1 offset: -1; array: no
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 54: query: select json ( ' 1 ' :: json with unique keys ) into json; with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 54: using PQexec
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_check_PQresult on line 54: bad response - ERROR: cannot use non-string types with WITH UNIQUE KEYS clause
+LINE 1: select json ( ' 1 ' :: json with unique keys ) into json
+ ^
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: raising sqlstate 42804 (sqlcode -400): cannot use non-string types with WITH UNIQUE KEYS clause on line 54
+[NO_PID]: sqlca: code: -400, state: 42804
+SQL error: cannot use non-string types with WITH UNIQUE KEYS clause on line 54
+[NO_PID]: ecpg_execute on line 57: query: select json ( '{"a": 1, "a": 2}' ); with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 57: using PQexec
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 57: correctly got 1 tuples with 1 fields
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_get_data on line 57: RESULT: {"a": 1, "a": 2} offset: -1; array: no
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 60: query: select json ( '{"a": 1, "a": 2}' with unique keys ); with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 60: using PQexec
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_check_PQresult on line 60: bad response - ERROR: duplicate JSON object key value
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: raising sqlstate 22030 (sqlcode -400): duplicate JSON object key value on line 60
+[NO_PID]: sqlca: code: -400, state: 22030
+SQL error: duplicate JSON object key value on line 60
+[NO_PID]: ecpg_execute on line 63: query: select json_scalar ( null ); with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 63: using PQexec
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 63: correctly got 1 tuples with 1 fields
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_get_data on line 63: RESULT: offset: -1; array: no
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: raising sqlcode -213 on line 63: null value without indicator on line 63
+[NO_PID]: sqlca: code: -213, state: 22002
+SQL error: null value without indicator on line 63
+[NO_PID]: ecpg_execute on line 66: query: select json_scalar ( null :: int ); with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 66: using PQexec
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 66: correctly got 1 tuples with 1 fields
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_get_data on line 66: RESULT: offset: -1; array: no
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: raising sqlcode -213 on line 66: null value without indicator on line 66
+[NO_PID]: sqlca: code: -213, state: 22002
+SQL error: null value without indicator on line 66
+[NO_PID]: ecpg_execute on line 69: query: select json_scalar ( 123.45 ); with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 69: using PQexec
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 69: correctly got 1 tuples with 1 fields
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_get_data on line 69: RESULT: 123.45 offset: -1; array: no
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 72: query: select json_scalar ( true ); with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 72: using PQexec
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 72: correctly got 1 tuples with 1 fields
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_get_data on line 72: RESULT: true offset: -1; array: no
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 75: query: select json_scalar ( ' 123.45' ); with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 75: using PQexec
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 75: correctly got 1 tuples with 1 fields
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_get_data on line 75: RESULT: " 123.45" offset: -1; array: no
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 78: query: select json_scalar ( '2020-06-07 01:02:03' :: timestamp ); with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 78: using PQexec
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 78: correctly got 1 tuples with 1 fields
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_get_data on line 78: RESULT: "2020-06-07T01:02:03" offset: -1; array: no
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 81: query: select json_scalar ( '{}' :: jsonb ); with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 81: using PQexec
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 81: correctly got 1 tuples with 1 fields
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_get_data on line 81: RESULT: {} offset: -1; array: no
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 84: query: select json_serialize ( null ); with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 84: using PQexec
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 84: correctly got 1 tuples with 1 fields
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_get_data on line 84: RESULT: offset: -1; array: no
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: raising sqlcode -213 on line 84: null value without indicator on line 84
+[NO_PID]: sqlca: code: -213, state: 22002
+SQL error: null value without indicator on line 84
+[NO_PID]: ecpg_execute on line 87: query: select json_serialize ( json ( '{ "a" : 1 } ' ) ); with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 87: using PQexec
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 87: correctly got 1 tuples with 1 fields
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_get_data on line 87: RESULT: { "a" : 1 } offset: -1; array: no
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 90: query: select json_serialize ( '{ "a" : 1 } ' ); with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 90: using PQexec
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 90: correctly got 1 tuples with 1 fields
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_get_data on line 90: RESULT: { "a" : 1 } offset: -1; array: no
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 93: query: select json_serialize ( '1' format json ); with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 93: using PQexec
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 93: correctly got 1 tuples with 1 fields
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_get_data on line 93: RESULT: 1 offset: -1; array: no
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 96: query: select json_serialize ( '{ "a" : 1 } ' returning varchar ); with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 96: using PQexec
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 96: correctly got 1 tuples with 1 fields
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_get_data on line 96: RESULT: { "a" : 1 } offset: -1; array: no
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 99: query: select json_serialize ( '{ "a" : 1 } ' returning jsonb ); with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 99: using PQexec
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_check_PQresult on line 99: bad response - ERROR: cannot use RETURNING type jsonb in JSON_SERIALIZE()
+HINT: Try returning a string type or bytea.
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: raising sqlstate 42804 (sqlcode -400): cannot use RETURNING type jsonb in JSON_SERIALIZE() on line 99
+[NO_PID]: sqlca: code: -400, state: 42804
+SQL error: cannot use RETURNING type jsonb in JSON_SERIALIZE() on line 99
+[NO_PID]: ecpg_execute on line 102: query: with val ( js ) as ( values ( '{ "a": 1, "b": [{ "a": 1, "b": 0, "a": 2 }] }' ) ) select js is json "IS JSON" , js is not json "IS NOT JSON" , js is json value "IS VALUE" , js is json object "IS OBJECT" , js is json array "IS ARRAY" , js is json scalar "IS SCALAR" , js is json without unique keys "WITHOUT UNIQUE" , js is json with unique keys "WITH UNIQUE" from val; with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 102: using PQexec
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 102: correctly got 1 tuples with 8 fields
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_get_data on line 102: RESULT: t offset: -1; array: no
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_get_data on line 102: RESULT: f offset: -1; array: no
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_get_data on line 102: RESULT: t offset: -1; array: no
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_get_data on line 102: RESULT: t offset: -1; array: no
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_get_data on line 102: RESULT: f offset: -1; array: no
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_get_data on line 102: RESULT: f offset: -1; array: no
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_get_data on line 102: RESULT: t offset: -1; array: no
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_get_data on line 102: RESULT: f offset: -1; array: no
[NO_PID]: sqlca: code: 0, state: 00000
[NO_PID]: ecpg_finish: connection ecpg1_regression closed
[NO_PID]: sqlca: code: 0, state: 00000
Found json=[]
Found json={"1" : 1, "1" : "2"}
Found json={"1": 1}
+Found json={"1": 1}
+Found json={ "a" : 1 }
+Found json=1
+Found json={"a": 1, "a": 2}
+Found json={"a": 1, "a": 2}
+Found json={"a": 1, "a": 2}
+Found json=123.45
+Found json=true
+Found json=" 123.45"
+Found json="2020-06-07T01:02:03"
+Found json={}
+Found json={}
+Found json={ "a" : 1 }
+Found json={ "a" : 1 }
+Found json=1
+Found json={ "a" : 1 }
Found is_json[0]: true
Found is_json[1]: false
Found is_json[2]: true
EXEC SQL SELECT JSON_OBJECT(1: 1, '2': NULL ABSENT ON NULL WITHOUT UNIQUE RETURNING jsonb) INTO :json;
printf("Found json=%s\n", json);
+ EXEC SQL SELECT JSON(NULL) INTO :json;
+ printf("Found json=%s\n", json);
+
+ EXEC SQL SELECT JSON('{ "a" : 1 } ' FORMAT JSON) INTO :json;
+ printf("Found json=%s\n", json);
+
+ EXEC SQL SELECT JSON('{ "a" : 1 } ' FORMAT JSON ENCODING UTF8) INTO :json;
+ // error
+
+ EXEC SQL SELECT JSON(' 1 '::jsonb) INTO :json;
+ printf("Found json=%s\n", json);
+
+ EXEC SQL SELECT JSON(' 1 '::json WITH UNIQUE KEYS) INTO json;
+ // error
+
+ EXEC SQL SELECT JSON('{"a": 1, "a": 2}') INTO :json;
+ printf("Found json=%s\n", json);
+
+ EXEC SQL SELECT JSON('{"a": 1, "a": 2}' WITH UNIQUE KEYS) INTO :json;
+ // error
+
+ EXEC SQL SELECT JSON_SCALAR(NULL) INTO :json;
+ printf("Found json=%s\n", json);
+
+ EXEC SQL SELECT JSON_SCALAR(NULL::int) INTO :json;
+ printf("Found json=%s\n", json);
+
+ EXEC SQL SELECT JSON_SCALAR(123.45) INTO :json;
+ printf("Found json=%s\n", json);
+
+ EXEC SQL SELECT JSON_SCALAR(true) INTO :json;
+ printf("Found json=%s\n", json);
+
+ EXEC SQL SELECT JSON_SCALAR(' 123.45') INTO :json;
+ printf("Found json=%s\n", json);
+
+ EXEC SQL SELECT JSON_SCALAR('2020-06-07 01:02:03'::timestamp) INTO :json;
+ printf("Found json=%s\n", json);
+
+ EXEC SQL SELECT JSON_SCALAR('{}'::jsonb) INTO :json;
+ printf("Found json=%s\n", json);
+
+ EXEC SQL SELECT JSON_SERIALIZE(NULL) INTO :json;
+ printf("Found json=%s\n", json);
+
+ EXEC SQL SELECT JSON_SERIALIZE(JSON('{ "a" : 1 } ')) INTO :json;
+ printf("Found json=%s\n", json);
+
+ EXEC SQL SELECT JSON_SERIALIZE('{ "a" : 1 } ') INTO :json;
+ printf("Found json=%s\n", json);
+
+ EXEC SQL SELECT JSON_SERIALIZE('1' FORMAT JSON) INTO :json;
+ printf("Found json=%s\n", json);
+
+ EXEC SQL SELECT JSON_SERIALIZE('{ "a" : 1 } ' RETURNING varchar) INTO :json;
+ printf("Found json=%s\n", json);
+
+ EXEC SQL SELECT JSON_SERIALIZE('{ "a" : 1 } ' RETURNING jsonb);
+ // error
+
EXEC SQL WITH val (js) AS (VALUES ('{ "a": 1, "b": [{ "a": 1, "b": 0, "a": 2 }] }'))
SELECT
js IS JSON "IS JSON",
+-- JSON()
+SELECT JSON();
+ERROR: syntax error at or near ")"
+LINE 1: SELECT JSON();
+ ^
+SELECT JSON(NULL);
+ json
+------
+
+(1 row)
+
+SELECT JSON('{ "a" : 1 } ');
+ json
+--------------
+ { "a" : 1 }
+(1 row)
+
+SELECT JSON('{ "a" : 1 } ' FORMAT JSON);
+ json
+--------------
+ { "a" : 1 }
+(1 row)
+
+SELECT JSON('{ "a" : 1 } ' FORMAT JSON ENCODING UTF8);
+ERROR: JSON ENCODING clause is only allowed for bytea input type
+LINE 1: SELECT JSON('{ "a" : 1 } ' FORMAT JSON ENCODING UTF8);
+ ^
+SELECT JSON('{ "a" : 1 } '::bytea FORMAT JSON ENCODING UTF8);
+ json
+--------------
+ { "a" : 1 }
+(1 row)
+
+SELECT pg_typeof(JSON('{ "a" : 1 } '));
+ pg_typeof
+-----------
+ json
+(1 row)
+
+SELECT JSON(' 1 '::json);
+ json
+---------
+ 1
+(1 row)
+
+SELECT JSON(' 1 '::jsonb);
+ json
+------
+ 1
+(1 row)
+
+SELECT JSON(' 1 '::json WITH UNIQUE KEYS);
+ERROR: cannot use non-string types with WITH UNIQUE KEYS clause
+LINE 1: SELECT JSON(' 1 '::json WITH UNIQUE KEYS);
+ ^
+SELECT JSON(123);
+ERROR: cannot cast type integer to json
+LINE 1: SELECT JSON(123);
+ ^
+SELECT JSON('{"a": 1, "a": 2}');
+ json
+------------------
+ {"a": 1, "a": 2}
+(1 row)
+
+SELECT JSON('{"a": 1, "a": 2}' WITH UNIQUE KEYS);
+ERROR: duplicate JSON object key value
+SELECT JSON('{"a": 1, "a": 2}' WITHOUT UNIQUE KEYS);
+ json
+------------------
+ {"a": 1, "a": 2}
+(1 row)
+
+EXPLAIN (VERBOSE, COSTS OFF) SELECT JSON('123');
+ QUERY PLAN
+-----------------------------
+ Result
+ Output: JSON('123'::json)
+(2 rows)
+
+EXPLAIN (VERBOSE, COSTS OFF) SELECT JSON('123' FORMAT JSON);
+ QUERY PLAN
+-----------------------------
+ Result
+ Output: JSON('123'::json)
+(2 rows)
+
+EXPLAIN (VERBOSE, COSTS OFF) SELECT JSON('123'::bytea FORMAT JSON);
+ QUERY PLAN
+-----------------------------------------------
+ Result
+ Output: JSON('\x313233'::bytea FORMAT JSON)
+(2 rows)
+
+EXPLAIN (VERBOSE, COSTS OFF) SELECT JSON('123'::bytea FORMAT JSON ENCODING UTF8);
+ QUERY PLAN
+-------------------------------------------------------------
+ Result
+ Output: JSON('\x313233'::bytea FORMAT JSON ENCODING UTF8)
+(2 rows)
+
+EXPLAIN (VERBOSE, COSTS OFF) SELECT JSON('123' WITH UNIQUE KEYS);
+ QUERY PLAN
+----------------------------------------------
+ Result
+ Output: JSON('123'::text WITH UNIQUE KEYS)
+(2 rows)
+
+EXPLAIN (VERBOSE, COSTS OFF) SELECT JSON('123' WITHOUT UNIQUE KEYS);
+ QUERY PLAN
+-----------------------------
+ Result
+ Output: JSON('123'::json)
+(2 rows)
+
+EXPLAIN (VERBOSE, COSTS OFF) SELECT JSON('123');
+ QUERY PLAN
+-----------------------------
+ Result
+ Output: JSON('123'::json)
+(2 rows)
+
+SELECT pg_typeof(JSON('123'));
+ pg_typeof
+-----------
+ json
+(1 row)
+
+-- JSON_SCALAR()
+SELECT JSON_SCALAR();
+ERROR: syntax error at or near ")"
+LINE 1: SELECT JSON_SCALAR();
+ ^
+SELECT JSON_SCALAR(NULL);
+ json_scalar
+-------------
+
+(1 row)
+
+SELECT JSON_SCALAR(NULL::int);
+ json_scalar
+-------------
+
+(1 row)
+
+SELECT JSON_SCALAR(123);
+ json_scalar
+-------------
+ 123
+(1 row)
+
+SELECT JSON_SCALAR(123.45);
+ json_scalar
+-------------
+ 123.45
+(1 row)
+
+SELECT JSON_SCALAR(123.45::numeric);
+ json_scalar
+-------------
+ 123.45
+(1 row)
+
+SELECT JSON_SCALAR(true);
+ json_scalar
+-------------
+ true
+(1 row)
+
+SELECT JSON_SCALAR(false);
+ json_scalar
+-------------
+ false
+(1 row)
+
+SELECT JSON_SCALAR(' 123.45');
+ json_scalar
+-------------
+ " 123.45"
+(1 row)
+
+SELECT JSON_SCALAR('2020-06-07'::date);
+ json_scalar
+--------------
+ "2020-06-07"
+(1 row)
+
+SELECT JSON_SCALAR('2020-06-07 01:02:03'::timestamp);
+ json_scalar
+-----------------------
+ "2020-06-07T01:02:03"
+(1 row)
+
+SELECT JSON_SCALAR('{}'::json);
+ json_scalar
+-------------
+ {}
+(1 row)
+
+SELECT JSON_SCALAR('{}'::jsonb);
+ json_scalar
+-------------
+ {}
+(1 row)
+
+EXPLAIN (VERBOSE, COSTS OFF) SELECT JSON_SCALAR(123);
+ QUERY PLAN
+----------------------------
+ Result
+ Output: JSON_SCALAR(123)
+(2 rows)
+
+EXPLAIN (VERBOSE, COSTS OFF) SELECT JSON_SCALAR('123');
+ QUERY PLAN
+------------------------------------
+ Result
+ Output: JSON_SCALAR('123'::text)
+(2 rows)
+
+-- JSON_SERIALIZE()
+SELECT JSON_SERIALIZE();
+ERROR: syntax error at or near ")"
+LINE 1: SELECT JSON_SERIALIZE();
+ ^
+SELECT JSON_SERIALIZE(NULL);
+ json_serialize
+----------------
+
+(1 row)
+
+SELECT JSON_SERIALIZE(JSON('{ "a" : 1 } '));
+ json_serialize
+----------------
+ { "a" : 1 }
+(1 row)
+
+SELECT JSON_SERIALIZE('{ "a" : 1 } ');
+ json_serialize
+----------------
+ { "a" : 1 }
+(1 row)
+
+SELECT JSON_SERIALIZE('1');
+ json_serialize
+----------------
+ 1
+(1 row)
+
+SELECT JSON_SERIALIZE('1' FORMAT JSON);
+ json_serialize
+----------------
+ 1
+(1 row)
+
+SELECT JSON_SERIALIZE('{ "a" : 1 } ' RETURNING bytea);
+ json_serialize
+----------------------------
+ \x7b20226122203a2031207d20
+(1 row)
+
+SELECT JSON_SERIALIZE('{ "a" : 1 } ' RETURNING varchar);
+ json_serialize
+----------------
+ { "a" : 1 }
+(1 row)
+
+SELECT pg_typeof(JSON_SERIALIZE(NULL));
+ pg_typeof
+-----------
+ text
+(1 row)
+
+-- only string types or bytea allowed
+SELECT JSON_SERIALIZE('{ "a" : 1 } ' RETURNING jsonb);
+ERROR: cannot use RETURNING type jsonb in JSON_SERIALIZE()
+HINT: Try returning a string type or bytea.
+EXPLAIN (VERBOSE, COSTS OFF) SELECT JSON_SERIALIZE('{}');
+ QUERY PLAN
+-----------------------------------------------------
+ Result
+ Output: JSON_SERIALIZE('{}'::json RETURNING text)
+(2 rows)
+
+EXPLAIN (VERBOSE, COSTS OFF) SELECT JSON_SERIALIZE('{}' RETURNING bytea);
+ QUERY PLAN
+------------------------------------------------------
+ Result
+ Output: JSON_SERIALIZE('{}'::json RETURNING bytea)
+(2 rows)
+
-- JSON_OBJECT()
SELECT JSON_OBJECT();
json_object
SELECT JSON_OBJECTAGG(k: v ABSENT ON NULL WITH UNIQUE KEYS RETURNING jsonb)
FROM (VALUES (1, 1), (1, NULL), (2, 2)) foo(k, v);
ERROR: duplicate JSON object key
+SELECT JSON_OBJECTAGG(k: v ABSENT ON NULL WITH UNIQUE KEYS RETURNING jsonb)
+FROM (VALUES (1, 1), (0, NULL),(4, null), (5, null),(6, null),(2, 2)) foo(k, v);
+ json_objectagg
+------------------
+ {"1": 1, "2": 2}
+(1 row)
+
-- Test JSON_OBJECT deparsing
EXPLAIN (VERBOSE, COSTS OFF)
SELECT JSON_OBJECT('foo' : '1' FORMAT JSON, 'bar' : 'baz' RETURNING json);
CREATE OR REPLACE VIEW public.json_object_view AS
SELECT JSON_OBJECT('foo' : '1'::text FORMAT JSON, 'bar' : 'baz'::text RETURNING json) AS "json_object"
DROP VIEW json_object_view;
+SELECT to_json(a) AS a, JSON_OBJECTAGG(k : v WITH UNIQUE KEYS) OVER (ORDER BY k)
+FROM (VALUES (1,1), (2,2)) a(k,v);
+ a | json_objectagg
+---------------+----------------------
+ {"k":1,"v":1} | { "1" : 1 }
+ {"k":2,"v":2} | { "1" : 1, "2" : 2 }
+(2 rows)
+
+SELECT to_json(a) AS a, JSON_OBJECTAGG(k : v WITH UNIQUE KEYS) OVER (ORDER BY k)
+FROM (VALUES (1,1), (1,2), (2,2)) a(k,v);
+ERROR: duplicate JSON key "1"
+SELECT to_json(a) AS a, JSON_OBJECTAGG(k : v ABSENT ON NULL WITH UNIQUE KEYS)
+ OVER (ORDER BY k)
+FROM (VALUES (1,1), (1,null), (2,2)) a(k,v);
+ERROR: duplicate JSON key "1"
+SELECT to_json(a) AS a, JSON_OBJECTAGG(k : v ABSENT ON NULL)
+OVER (ORDER BY k)
+FROM (VALUES (1,1), (1,null), (2,2)) a(k,v);
+ a | json_objectagg
+------------------+----------------------
+ {"k":1,"v":1} | { "1" : 1 }
+ {"k":1,"v":null} | { "1" : 1 }
+ {"k":2,"v":2} | { "1" : 1, "2" : 2 }
+(3 rows)
+
+SELECT to_json(a) AS a, JSON_OBJECTAGG(k : v ABSENT ON NULL)
+OVER (ORDER BY k RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)
+FROM (VALUES (1,1), (1,null), (2,2)) a(k,v);
+ a | json_objectagg
+------------------+----------------------
+ {"k":1,"v":1} | { "1" : 1, "2" : 2 }
+ {"k":1,"v":null} | { "1" : 1, "2" : 2 }
+ {"k":2,"v":2} | { "1" : 1, "2" : 2 }
+(3 rows)
+
-- Test JSON_ARRAY deparsing
EXPLAIN (VERBOSE, COSTS OFF)
SELECT JSON_ARRAY('1' FORMAT JSON, 2 RETURNING json);
+-- JSON()
+SELECT JSON();
+SELECT JSON(NULL);
+SELECT JSON('{ "a" : 1 } ');
+SELECT JSON('{ "a" : 1 } ' FORMAT JSON);
+SELECT JSON('{ "a" : 1 } ' FORMAT JSON ENCODING UTF8);
+SELECT JSON('{ "a" : 1 } '::bytea FORMAT JSON ENCODING UTF8);
+SELECT pg_typeof(JSON('{ "a" : 1 } '));
+
+SELECT JSON(' 1 '::json);
+SELECT JSON(' 1 '::jsonb);
+SELECT JSON(' 1 '::json WITH UNIQUE KEYS);
+SELECT JSON(123);
+
+SELECT JSON('{"a": 1, "a": 2}');
+SELECT JSON('{"a": 1, "a": 2}' WITH UNIQUE KEYS);
+SELECT JSON('{"a": 1, "a": 2}' WITHOUT UNIQUE KEYS);
+
+EXPLAIN (VERBOSE, COSTS OFF) SELECT JSON('123');
+EXPLAIN (VERBOSE, COSTS OFF) SELECT JSON('123' FORMAT JSON);
+EXPLAIN (VERBOSE, COSTS OFF) SELECT JSON('123'::bytea FORMAT JSON);
+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);
+
+EXPLAIN (VERBOSE, COSTS OFF) SELECT JSON('123');
+SELECT pg_typeof(JSON('123'));
+
+-- JSON_SCALAR()
+SELECT JSON_SCALAR();
+SELECT JSON_SCALAR(NULL);
+SELECT JSON_SCALAR(NULL::int);
+SELECT JSON_SCALAR(123);
+SELECT JSON_SCALAR(123.45);
+SELECT JSON_SCALAR(123.45::numeric);
+SELECT JSON_SCALAR(true);
+SELECT JSON_SCALAR(false);
+SELECT JSON_SCALAR(' 123.45');
+SELECT JSON_SCALAR('2020-06-07'::date);
+SELECT JSON_SCALAR('2020-06-07 01:02:03'::timestamp);
+SELECT JSON_SCALAR('{}'::json);
+SELECT JSON_SCALAR('{}'::jsonb);
+
+EXPLAIN (VERBOSE, COSTS OFF) SELECT JSON_SCALAR(123);
+EXPLAIN (VERBOSE, COSTS OFF) SELECT JSON_SCALAR('123');
+
+-- JSON_SERIALIZE()
+SELECT JSON_SERIALIZE();
+SELECT JSON_SERIALIZE(NULL);
+SELECT JSON_SERIALIZE(JSON('{ "a" : 1 } '));
+SELECT JSON_SERIALIZE('{ "a" : 1 } ');
+SELECT JSON_SERIALIZE('1');
+SELECT JSON_SERIALIZE('1' FORMAT JSON);
+SELECT JSON_SERIALIZE('{ "a" : 1 } ' RETURNING bytea);
+SELECT JSON_SERIALIZE('{ "a" : 1 } ' RETURNING varchar);
+SELECT pg_typeof(JSON_SERIALIZE(NULL));
+
+-- only string types or bytea allowed
+SELECT JSON_SERIALIZE('{ "a" : 1 } ' RETURNING jsonb);
+
+
+EXPLAIN (VERBOSE, COSTS OFF) SELECT JSON_SERIALIZE('{}');
+EXPLAIN (VERBOSE, COSTS OFF) SELECT JSON_SERIALIZE('{}' RETURNING bytea);
+
-- JSON_OBJECT()
SELECT JSON_OBJECT();
SELECT JSON_OBJECT(RETURNING json);
SELECT JSON_OBJECTAGG(k: v ABSENT ON NULL WITH UNIQUE KEYS RETURNING jsonb)
FROM (VALUES (1, 1), (1, NULL), (2, 2)) foo(k, v);
+SELECT JSON_OBJECTAGG(k: v ABSENT ON NULL WITH UNIQUE KEYS RETURNING jsonb)
+FROM (VALUES (1, 1), (0, NULL),(4, null), (5, null),(6, null),(2, 2)) foo(k, v);
+
-- Test JSON_OBJECT deparsing
EXPLAIN (VERBOSE, COSTS OFF)
SELECT JSON_OBJECT('foo' : '1' FORMAT JSON, 'bar' : 'baz' RETURNING json);
DROP VIEW json_object_view;
+SELECT to_json(a) AS a, JSON_OBJECTAGG(k : v WITH UNIQUE KEYS) OVER (ORDER BY k)
+FROM (VALUES (1,1), (2,2)) a(k,v);
+
+SELECT to_json(a) AS a, JSON_OBJECTAGG(k : v WITH UNIQUE KEYS) OVER (ORDER BY k)
+FROM (VALUES (1,1), (1,2), (2,2)) a(k,v);
+
+SELECT to_json(a) AS a, JSON_OBJECTAGG(k : v ABSENT ON NULL WITH UNIQUE KEYS)
+ OVER (ORDER BY k)
+FROM (VALUES (1,1), (1,null), (2,2)) a(k,v);
+
+SELECT to_json(a) AS a, JSON_OBJECTAGG(k : v ABSENT ON NULL)
+OVER (ORDER BY k)
+FROM (VALUES (1,1), (1,null), (2,2)) a(k,v);
+
+SELECT to_json(a) AS a, JSON_OBJECTAGG(k : v ABSENT ON NULL)
+OVER (ORDER BY k RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)
+FROM (VALUES (1,1), (1,null), (2,2)) a(k,v);
+
-- Test JSON_ARRAY deparsing
EXPLAIN (VERBOSE, COSTS OFF)
SELECT JSON_ARRAY('1' FORMAT JSON, 2 RETURNING json);
JsonObjectAgg
JsonObjectConstructor
JsonOutput
+JsonParseExpr
JsonParseContext
JsonParseErrorType
JsonPath
JsonPathPredicateCallback
JsonPathString
JsonReturning
+JsonScalarExpr
JsonSemAction
+JsonSerializeExpr
JsonTokenType
JsonTransformStringValuesAction
JsonTypeCategory