mirror of
https://github.com/postgres/postgres.git
synced 2025-04-21 12:05:57 +03:00
SQL/JSON constructors
This patch introduces the SQL/JSON standard constructors for JSON: JSON() JSON_ARRAY() JSON_ARRAYAGG() JSON_OBJECT() JSON_OBJECTAGG() For the most part these functions provide facilities that mimic existing json/jsonb functions. However, they also offer some useful additional functionality. In addition to text input, the JSON() function accepts bytea input, which it will decode and constuct a json value from. The other functions provide useful options for handling duplicate keys and null values. This series of patches will be followed by a consolidated documentation patch. 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
This commit is contained in:
parent
f79b803dcc
commit
f4fb45d15c
@ -2450,6 +2450,69 @@ ExecInitExprRec(Expr *node, ExprState *state,
|
||||
break;
|
||||
}
|
||||
|
||||
case T_JsonConstructorExpr:
|
||||
{
|
||||
JsonConstructorExpr *ctor = (JsonConstructorExpr *) node;
|
||||
List *args = ctor->args;
|
||||
ListCell *lc;
|
||||
int nargs = list_length(args);
|
||||
int argno = 0;
|
||||
|
||||
if (ctor->func)
|
||||
{
|
||||
ExecInitExprRec(ctor->func, state, resv, resnull);
|
||||
}
|
||||
else
|
||||
{
|
||||
scratch.opcode = EEOP_JSON_CONSTRUCTOR;
|
||||
scratch.d.json_constructor.constructor = ctor;
|
||||
scratch.d.json_constructor.arg_values = palloc(sizeof(Datum) * nargs);
|
||||
scratch.d.json_constructor.arg_nulls = palloc(sizeof(bool) * nargs);
|
||||
scratch.d.json_constructor.arg_types = palloc(sizeof(Oid) * nargs);
|
||||
scratch.d.json_constructor.nargs = nargs;
|
||||
|
||||
foreach(lc, args)
|
||||
{
|
||||
Expr *arg = (Expr *) lfirst(lc);
|
||||
|
||||
scratch.d.json_constructor.arg_types[argno] = exprType((Node *) arg);
|
||||
|
||||
if (IsA(arg, Const))
|
||||
{
|
||||
/* Don't evaluate const arguments every round */
|
||||
Const *con = (Const *) arg;
|
||||
|
||||
scratch.d.json_constructor.arg_values[argno] = con->constvalue;
|
||||
scratch.d.json_constructor.arg_nulls[argno] = con->constisnull;
|
||||
}
|
||||
else
|
||||
{
|
||||
ExecInitExprRec(arg, state,
|
||||
&scratch.d.json_constructor.arg_values[argno],
|
||||
&scratch.d.json_constructor.arg_nulls[argno]);
|
||||
}
|
||||
argno++;
|
||||
}
|
||||
|
||||
ExprEvalPushStep(state, &scratch);
|
||||
}
|
||||
|
||||
if (ctor->coercion)
|
||||
{
|
||||
Datum *innermost_caseval = state->innermost_caseval;
|
||||
bool *innermost_isnull = state->innermost_casenull;
|
||||
|
||||
state->innermost_caseval = resv;
|
||||
state->innermost_casenull = resnull;
|
||||
|
||||
ExecInitExprRec(ctor->coercion, state, resv, resnull);
|
||||
|
||||
state->innermost_caseval = innermost_caseval;
|
||||
state->innermost_casenull = innermost_isnull;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
elog(ERROR, "unrecognized node type: %d",
|
||||
(int) nodeTag(node));
|
||||
|
@ -71,6 +71,8 @@
|
||||
#include "utils/date.h"
|
||||
#include "utils/datum.h"
|
||||
#include "utils/expandedrecord.h"
|
||||
#include "utils/json.h"
|
||||
#include "utils/jsonb.h"
|
||||
#include "utils/lsyscache.h"
|
||||
#include "utils/memutils.h"
|
||||
#include "utils/timestamp.h"
|
||||
@ -477,6 +479,7 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
|
||||
&&CASE_EEOP_GROUPING_FUNC,
|
||||
&&CASE_EEOP_WINDOW_FUNC,
|
||||
&&CASE_EEOP_SUBPLAN,
|
||||
&&CASE_EEOP_JSON_CONSTRUCTOR,
|
||||
&&CASE_EEOP_AGG_STRICT_DESERIALIZE,
|
||||
&&CASE_EEOP_AGG_DESERIALIZE,
|
||||
&&CASE_EEOP_AGG_STRICT_INPUT_CHECK_ARGS,
|
||||
@ -1786,7 +1789,13 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
|
||||
{
|
||||
/* too complex for an inline implementation */
|
||||
ExecEvalAggOrderedTransTuple(state, op, econtext);
|
||||
EEO_NEXT();
|
||||
}
|
||||
|
||||
EEO_CASE(EEOP_JSON_CONSTRUCTOR)
|
||||
{
|
||||
/* too complex for an inline implementation */
|
||||
ExecEvalJsonConstructor(state, op, econtext);
|
||||
EEO_NEXT();
|
||||
}
|
||||
|
||||
@ -4380,3 +4389,42 @@ ExecAggPlainTransByRef(AggState *aggstate, AggStatePerTrans pertrans,
|
||||
|
||||
MemoryContextSwitchTo(oldContext);
|
||||
}
|
||||
|
||||
/*
|
||||
* Evaluate a JSON constructor expression.
|
||||
*/
|
||||
void
|
||||
ExecEvalJsonConstructor(ExprState *state, ExprEvalStep *op,
|
||||
ExprContext *econtext)
|
||||
{
|
||||
Datum res;
|
||||
JsonConstructorExpr *ctor = op->d.json_constructor.constructor;
|
||||
bool is_jsonb = ctor->returning->format->format_type == JS_FORMAT_JSONB;
|
||||
bool isnull = false;
|
||||
|
||||
if (ctor->type == JSCTOR_JSON_ARRAY)
|
||||
res = (is_jsonb ?
|
||||
jsonb_build_array_worker :
|
||||
json_build_array_worker)(op->d.json_constructor.nargs,
|
||||
op->d.json_constructor.arg_values,
|
||||
op->d.json_constructor.arg_nulls,
|
||||
op->d.json_constructor.arg_types,
|
||||
op->d.json_constructor.constructor->absent_on_null);
|
||||
else if (ctor->type == JSCTOR_JSON_OBJECT)
|
||||
res = (is_jsonb ?
|
||||
jsonb_build_object_worker :
|
||||
json_build_object_worker)(op->d.json_constructor.nargs,
|
||||
op->d.json_constructor.arg_values,
|
||||
op->d.json_constructor.arg_nulls,
|
||||
op->d.json_constructor.arg_types,
|
||||
op->d.json_constructor.constructor->absent_on_null,
|
||||
op->d.json_constructor.constructor->unique);
|
||||
else
|
||||
{
|
||||
res = (Datum) 0;
|
||||
elog(ERROR, "invalid JsonConstructorExpr type %d", ctor->type);
|
||||
}
|
||||
|
||||
*op->resvalue = res;
|
||||
*op->resnull = isnull;
|
||||
}
|
||||
|
@ -2348,6 +2348,12 @@ llvm_compile_expr(ExprState *state)
|
||||
LLVMBuildBr(b, opblocks[opno + 1]);
|
||||
break;
|
||||
|
||||
case EEOP_JSON_CONSTRUCTOR:
|
||||
build_EvalXFunc(b, mod, "ExecEvalJsonConstructor",
|
||||
v_state, op, v_econtext);
|
||||
LLVMBuildBr(b, opblocks[opno + 1]);
|
||||
break;
|
||||
|
||||
case EEOP_LAST:
|
||||
Assert(false);
|
||||
break;
|
||||
|
@ -131,6 +131,7 @@ void *referenced_functions[] =
|
||||
ExecEvalSysVar,
|
||||
ExecEvalWholeRowVar,
|
||||
ExecEvalXmlExpr,
|
||||
ExecEvalJsonConstructor,
|
||||
MakeExpandedObjectReadOnlyInternal,
|
||||
slot_getmissingattrs,
|
||||
slot_getsomeattrs_int,
|
||||
|
@ -2344,6 +2344,152 @@ _copyJsonValueExpr(const JsonValueExpr *from)
|
||||
return newnode;
|
||||
}
|
||||
|
||||
/*
|
||||
* _copyJsonConstructorExpr
|
||||
*/
|
||||
static JsonConstructorExpr *
|
||||
_copyJsonConstructorExpr(const JsonConstructorExpr *from)
|
||||
{
|
||||
JsonConstructorExpr *newnode = makeNode(JsonConstructorExpr);
|
||||
|
||||
COPY_SCALAR_FIELD(type);
|
||||
COPY_NODE_FIELD(args);
|
||||
COPY_NODE_FIELD(func);
|
||||
COPY_NODE_FIELD(coercion);
|
||||
COPY_NODE_FIELD(returning);
|
||||
COPY_SCALAR_FIELD(absent_on_null);
|
||||
COPY_SCALAR_FIELD(unique);
|
||||
COPY_LOCATION_FIELD(location);
|
||||
|
||||
return newnode;
|
||||
}
|
||||
|
||||
/*
|
||||
* _copyJsonKeyValue
|
||||
*/
|
||||
static JsonKeyValue *
|
||||
_copyJsonKeyValue(const JsonKeyValue *from)
|
||||
{
|
||||
JsonKeyValue *newnode = makeNode(JsonKeyValue);
|
||||
|
||||
COPY_NODE_FIELD(key);
|
||||
COPY_NODE_FIELD(value);
|
||||
|
||||
return newnode;
|
||||
}
|
||||
|
||||
/*
|
||||
* _copyJsonObjectConstructor
|
||||
*/
|
||||
static JsonObjectConstructor *
|
||||
_copyJsonObjectConstructor(const JsonObjectConstructor *from)
|
||||
{
|
||||
JsonObjectConstructor *newnode = makeNode(JsonObjectConstructor);
|
||||
|
||||
COPY_NODE_FIELD(exprs);
|
||||
COPY_NODE_FIELD(output);
|
||||
COPY_SCALAR_FIELD(absent_on_null);
|
||||
COPY_SCALAR_FIELD(unique);
|
||||
COPY_LOCATION_FIELD(location);
|
||||
|
||||
return newnode;
|
||||
}
|
||||
|
||||
/*
|
||||
* _copyJsonAggConstructor
|
||||
*/
|
||||
static JsonAggConstructor *
|
||||
_copyJsonAggConstructor(const JsonAggConstructor *from)
|
||||
{
|
||||
JsonAggConstructor *newnode = makeNode(JsonAggConstructor);
|
||||
|
||||
COPY_NODE_FIELD(output);
|
||||
COPY_NODE_FIELD(agg_filter);
|
||||
COPY_NODE_FIELD(agg_order);
|
||||
COPY_NODE_FIELD(over);
|
||||
COPY_LOCATION_FIELD(location);
|
||||
|
||||
return newnode;
|
||||
}
|
||||
|
||||
/*
|
||||
* _copyJsonObjectAgg
|
||||
*/
|
||||
static JsonObjectAgg *
|
||||
_copyJsonObjectAgg(const JsonObjectAgg *from)
|
||||
{
|
||||
JsonObjectAgg *newnode = makeNode(JsonObjectAgg);
|
||||
|
||||
COPY_NODE_FIELD(constructor);
|
||||
COPY_NODE_FIELD(arg);
|
||||
COPY_SCALAR_FIELD(absent_on_null);
|
||||
COPY_SCALAR_FIELD(unique);
|
||||
|
||||
return newnode;
|
||||
}
|
||||
|
||||
/*
|
||||
* _copyJsonOutput
|
||||
*/
|
||||
static JsonOutput *
|
||||
_copyJsonOutput(const JsonOutput *from)
|
||||
{
|
||||
JsonOutput *newnode = makeNode(JsonOutput);
|
||||
|
||||
COPY_NODE_FIELD(typeName);
|
||||
COPY_NODE_FIELD(returning);
|
||||
|
||||
return newnode;
|
||||
}
|
||||
|
||||
/*
|
||||
* _copyJsonArrayConstructor
|
||||
*/
|
||||
static JsonArrayConstructor *
|
||||
_copyJsonArrayConstructor(const JsonArrayConstructor *from)
|
||||
{
|
||||
JsonArrayConstructor *newnode = makeNode(JsonArrayConstructor);
|
||||
|
||||
COPY_NODE_FIELD(exprs);
|
||||
COPY_NODE_FIELD(output);
|
||||
COPY_SCALAR_FIELD(absent_on_null);
|
||||
COPY_LOCATION_FIELD(location);
|
||||
|
||||
return newnode;
|
||||
}
|
||||
|
||||
/*
|
||||
* _copyJsonArrayAgg
|
||||
*/
|
||||
static JsonArrayAgg *
|
||||
_copyJsonArrayAgg(const JsonArrayAgg *from)
|
||||
{
|
||||
JsonArrayAgg *newnode = makeNode(JsonArrayAgg);
|
||||
|
||||
COPY_NODE_FIELD(constructor);
|
||||
COPY_NODE_FIELD(arg);
|
||||
COPY_SCALAR_FIELD(absent_on_null);
|
||||
|
||||
return newnode;
|
||||
}
|
||||
|
||||
/*
|
||||
* _copyJsonArrayQueryConstructor
|
||||
*/
|
||||
static JsonArrayQueryConstructor *
|
||||
_copyJsonArrayQueryConstructor(const JsonArrayQueryConstructor *from)
|
||||
{
|
||||
JsonArrayQueryConstructor *newnode = makeNode(JsonArrayQueryConstructor);
|
||||
|
||||
COPY_NODE_FIELD(query);
|
||||
COPY_NODE_FIELD(output);
|
||||
COPY_NODE_FIELD(format);
|
||||
COPY_SCALAR_FIELD(absent_on_null);
|
||||
COPY_LOCATION_FIELD(location);
|
||||
|
||||
return newnode;
|
||||
}
|
||||
|
||||
/* ****************************************************************
|
||||
* pathnodes.h copy functions
|
||||
*
|
||||
@ -5406,6 +5552,33 @@ copyObjectImpl(const void *from)
|
||||
case T_JsonValueExpr:
|
||||
retval = _copyJsonValueExpr(from);
|
||||
break;
|
||||
case T_JsonKeyValue:
|
||||
retval = _copyJsonKeyValue(from);
|
||||
break;
|
||||
case T_JsonConstructorExpr:
|
||||
retval = _copyJsonConstructorExpr(from);
|
||||
break;
|
||||
case T_JsonObjectConstructor:
|
||||
retval = _copyJsonObjectConstructor(from);
|
||||
break;
|
||||
case T_JsonAggConstructor:
|
||||
retval = _copyJsonAggConstructor(from);
|
||||
break;
|
||||
case T_JsonObjectAgg:
|
||||
retval = _copyJsonObjectAgg(from);
|
||||
break;
|
||||
case T_JsonOutput:
|
||||
retval = _copyJsonOutput(from);
|
||||
break;
|
||||
case T_JsonArrayConstructor:
|
||||
retval = _copyJsonArrayConstructor(from);
|
||||
break;
|
||||
case T_JsonArrayQueryConstructor:
|
||||
retval = _copyJsonArrayQueryConstructor(from);
|
||||
break;
|
||||
case T_JsonArrayAgg:
|
||||
retval = _copyJsonArrayAgg(from);
|
||||
break;
|
||||
|
||||
/*
|
||||
* RELATION NODES
|
||||
|
@ -871,6 +871,111 @@ _equalJsonValueExpr(const JsonValueExpr *a, const JsonValueExpr *b)
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
_equalJsonConstructorExpr(const JsonConstructorExpr *a, const JsonConstructorExpr *b)
|
||||
{
|
||||
COMPARE_SCALAR_FIELD(type);
|
||||
COMPARE_NODE_FIELD(args);
|
||||
COMPARE_NODE_FIELD(func);
|
||||
COMPARE_NODE_FIELD(coercion);
|
||||
COMPARE_NODE_FIELD(returning);
|
||||
COMPARE_SCALAR_FIELD(absent_on_null);
|
||||
COMPARE_SCALAR_FIELD(unique);
|
||||
COMPARE_LOCATION_FIELD(location);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
_equalJsonKeyValue(const JsonKeyValue *a, const JsonKeyValue *b)
|
||||
{
|
||||
COMPARE_NODE_FIELD(key);
|
||||
COMPARE_NODE_FIELD(value);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
_equalJsonObjectConstructor(const JsonObjectConstructor *a,
|
||||
const JsonObjectConstructor *b)
|
||||
{
|
||||
COMPARE_NODE_FIELD(exprs);
|
||||
COMPARE_NODE_FIELD(output);
|
||||
COMPARE_SCALAR_FIELD(absent_on_null);
|
||||
COMPARE_SCALAR_FIELD(unique);
|
||||
COMPARE_LOCATION_FIELD(location);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
_equalJsonAggConstructor(const JsonAggConstructor *a,
|
||||
const JsonAggConstructor *b)
|
||||
{
|
||||
COMPARE_NODE_FIELD(output);
|
||||
COMPARE_NODE_FIELD(agg_filter);
|
||||
COMPARE_NODE_FIELD(agg_order);
|
||||
COMPARE_NODE_FIELD(over);
|
||||
COMPARE_LOCATION_FIELD(location);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
_equalJsonObjectAgg(const JsonObjectAgg *a, const JsonObjectAgg *b)
|
||||
{
|
||||
COMPARE_NODE_FIELD(constructor);
|
||||
COMPARE_NODE_FIELD(arg);
|
||||
COMPARE_SCALAR_FIELD(absent_on_null);
|
||||
COMPARE_SCALAR_FIELD(unique);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
_equalJsonOutput(const JsonOutput *a, const JsonOutput *b)
|
||||
{
|
||||
COMPARE_NODE_FIELD(typeName);
|
||||
COMPARE_NODE_FIELD(returning);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
_equalJsonArrayConstructor(const JsonArrayConstructor *a,
|
||||
const JsonArrayConstructor *b)
|
||||
{
|
||||
COMPARE_NODE_FIELD(exprs);
|
||||
COMPARE_NODE_FIELD(output);
|
||||
COMPARE_SCALAR_FIELD(absent_on_null);
|
||||
COMPARE_LOCATION_FIELD(location);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
_equalJsonArrayAgg(const JsonArrayAgg *a, const JsonArrayAgg *b)
|
||||
{
|
||||
COMPARE_NODE_FIELD(constructor);
|
||||
COMPARE_NODE_FIELD(arg);
|
||||
COMPARE_SCALAR_FIELD(absent_on_null);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
_equalJsonArrayQueryConstructor(const JsonArrayQueryConstructor *a,
|
||||
const JsonArrayQueryConstructor *b)
|
||||
{
|
||||
COMPARE_NODE_FIELD(query);
|
||||
COMPARE_NODE_FIELD(output);
|
||||
COMPARE_NODE_FIELD(format);
|
||||
COMPARE_SCALAR_FIELD(absent_on_null);
|
||||
COMPARE_LOCATION_FIELD(location);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Stuff from pathnodes.h
|
||||
*/
|
||||
@ -3398,6 +3503,9 @@ equal(const void *a, const void *b)
|
||||
case T_JsonValueExpr:
|
||||
retval = _equalJsonValueExpr(a, b);
|
||||
break;
|
||||
case T_JsonConstructorExpr:
|
||||
retval = _equalJsonConstructorExpr(a, b);
|
||||
break;
|
||||
|
||||
/*
|
||||
* RELATION NODES
|
||||
@ -3978,6 +4086,30 @@ equal(const void *a, const void *b)
|
||||
case T_PublicationTable:
|
||||
retval = _equalPublicationTable(a, b);
|
||||
break;
|
||||
case T_JsonKeyValue:
|
||||
retval = _equalJsonKeyValue(a, b);
|
||||
break;
|
||||
case T_JsonObjectConstructor:
|
||||
retval = _equalJsonObjectConstructor(a, b);
|
||||
break;
|
||||
case T_JsonAggConstructor:
|
||||
retval = _equalJsonAggConstructor(a, b);
|
||||
break;
|
||||
case T_JsonObjectAgg:
|
||||
retval = _equalJsonObjectAgg(a, b);
|
||||
break;
|
||||
case T_JsonOutput:
|
||||
retval = _equalJsonOutput(a, b);
|
||||
break;
|
||||
case T_JsonArrayConstructor:
|
||||
retval = _equalJsonArrayConstructor(a, b);
|
||||
break;
|
||||
case T_JsonArrayQueryConstructor:
|
||||
retval = _equalJsonArrayQueryConstructor(a, b);
|
||||
break;
|
||||
case T_JsonArrayAgg:
|
||||
retval = _equalJsonArrayAgg(a, b);
|
||||
break;
|
||||
|
||||
default:
|
||||
elog(ERROR, "unrecognized node type: %d",
|
||||
|
@ -872,3 +872,18 @@ makeJsonEncoding(char *name)
|
||||
|
||||
return JS_ENC_DEFAULT;
|
||||
}
|
||||
|
||||
/*
|
||||
* makeJsonKeyValue -
|
||||
* creates a JsonKeyValue node
|
||||
*/
|
||||
Node *
|
||||
makeJsonKeyValue(Node *key, Node *value)
|
||||
{
|
||||
JsonKeyValue *n = makeNode(JsonKeyValue);
|
||||
|
||||
n->key = (Expr *) key;
|
||||
n->value = castNode(JsonValueExpr, value);
|
||||
|
||||
return (Node *) n;
|
||||
}
|
||||
|
@ -257,6 +257,9 @@ exprType(const Node *expr)
|
||||
type = exprType((Node *) (jve->formatted_expr ? jve->formatted_expr : jve->raw_expr));
|
||||
}
|
||||
break;
|
||||
case T_JsonConstructorExpr:
|
||||
type = ((const JsonConstructorExpr *) expr)->returning->typid;
|
||||
break;
|
||||
default:
|
||||
elog(ERROR, "unrecognized node type: %d", (int) nodeTag(expr));
|
||||
type = InvalidOid; /* keep compiler quiet */
|
||||
@ -491,6 +494,8 @@ exprTypmod(const Node *expr)
|
||||
return exprTypmod((Node *) ((const PlaceHolderVar *) expr)->phexpr);
|
||||
case T_JsonValueExpr:
|
||||
return exprTypmod((Node *) ((const JsonValueExpr *) expr)->formatted_expr);
|
||||
case T_JsonConstructorExpr:
|
||||
return -1; /* ((const JsonConstructorExpr *) expr)->returning->typmod; */
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -970,6 +975,16 @@ exprCollation(const Node *expr)
|
||||
case T_JsonValueExpr:
|
||||
coll = exprCollation((Node *) ((const JsonValueExpr *) expr)->formatted_expr);
|
||||
break;
|
||||
case T_JsonConstructorExpr:
|
||||
{
|
||||
const JsonConstructorExpr *ctor = (const JsonConstructorExpr *) expr;
|
||||
|
||||
if (ctor->coercion)
|
||||
coll = exprCollation((Node *) ctor->coercion);
|
||||
else
|
||||
coll = InvalidOid;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
elog(ERROR, "unrecognized node type: %d", (int) nodeTag(expr));
|
||||
coll = InvalidOid; /* keep compiler quiet */
|
||||
@ -1186,6 +1201,16 @@ exprSetCollation(Node *expr, Oid collation)
|
||||
exprSetCollation((Node *) ((JsonValueExpr *) expr)->formatted_expr,
|
||||
collation);
|
||||
break;
|
||||
case T_JsonConstructorExpr:
|
||||
{
|
||||
JsonConstructorExpr *ctor = (JsonConstructorExpr *) expr;
|
||||
|
||||
if (ctor->coercion)
|
||||
exprSetCollation((Node *) ctor->coercion, collation);
|
||||
else
|
||||
Assert(!OidIsValid(collation)); /* result is always a json[b] type */
|
||||
}
|
||||
break;
|
||||
default:
|
||||
elog(ERROR, "unrecognized node type: %d", (int) nodeTag(expr));
|
||||
break;
|
||||
@ -1635,6 +1660,9 @@ exprLocation(const Node *expr)
|
||||
case T_JsonValueExpr:
|
||||
loc = exprLocation((Node *) ((const JsonValueExpr *) expr)->raw_expr);
|
||||
break;
|
||||
case T_JsonConstructorExpr:
|
||||
loc = ((const JsonConstructorExpr *) expr)->location;
|
||||
break;
|
||||
default:
|
||||
/* for any other node type it's just unknown... */
|
||||
loc = -1;
|
||||
@ -2379,6 +2407,18 @@ expression_tree_walker(Node *node,
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case T_JsonConstructorExpr:
|
||||
{
|
||||
JsonConstructorExpr *ctor = (JsonConstructorExpr *) node;
|
||||
|
||||
if (walker(ctor->args, context))
|
||||
return true;
|
||||
if (walker(ctor->func, context))
|
||||
return true;
|
||||
if (walker(ctor->coercion, context))
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
elog(ERROR, "unrecognized node type: %d",
|
||||
(int) nodeTag(node));
|
||||
@ -3361,6 +3401,19 @@ expression_tree_mutator(Node *node,
|
||||
MUTATE(newnode->formatted_expr, jve->formatted_expr, Expr *);
|
||||
MUTATE(newnode->format, jve->format, JsonFormat *);
|
||||
|
||||
return (Node *) newnode;
|
||||
}
|
||||
case T_JsonConstructorExpr:
|
||||
{
|
||||
JsonConstructorExpr *jve = (JsonConstructorExpr *) node;
|
||||
JsonConstructorExpr *newnode;
|
||||
|
||||
FLATCOPY(newnode, jve, JsonConstructorExpr);
|
||||
MUTATE(newnode->args, jve->args, List *);
|
||||
MUTATE(newnode->func, jve->func, Expr *);
|
||||
MUTATE(newnode->coercion, jve->coercion, Expr *);
|
||||
MUTATE(newnode->returning, jve->returning, JsonReturning *);
|
||||
|
||||
return (Node *) newnode;
|
||||
}
|
||||
default:
|
||||
@ -3637,6 +3690,7 @@ raw_expression_tree_walker(Node *node,
|
||||
case T_ParamRef:
|
||||
case T_A_Const:
|
||||
case T_A_Star:
|
||||
case T_JsonFormat:
|
||||
/* primitive node types with no subnodes */
|
||||
break;
|
||||
case T_Alias:
|
||||
@ -4085,6 +4139,104 @@ raw_expression_tree_walker(Node *node,
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case T_JsonConstructorExpr:
|
||||
{
|
||||
JsonConstructorExpr *ctor = (JsonConstructorExpr *) node;
|
||||
|
||||
if (walker(ctor->args, context))
|
||||
return true;
|
||||
if (walker(ctor->func, context))
|
||||
return true;
|
||||
if (walker(ctor->coercion, context))
|
||||
return true;
|
||||
if (walker(ctor->returning, context))
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case T_JsonOutput:
|
||||
{
|
||||
JsonOutput *out = (JsonOutput *) node;
|
||||
|
||||
if (walker(out->typeName, context))
|
||||
return true;
|
||||
if (walker(out->returning, context))
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case T_JsonKeyValue:
|
||||
{
|
||||
JsonKeyValue *jkv = (JsonKeyValue *) node;
|
||||
|
||||
if (walker(jkv->key, context))
|
||||
return true;
|
||||
if (walker(jkv->value, context))
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case T_JsonObjectConstructor:
|
||||
{
|
||||
JsonObjectConstructor *joc = (JsonObjectConstructor *) node;
|
||||
|
||||
if (walker(joc->output, context))
|
||||
return true;
|
||||
if (walker(joc->exprs, context))
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case T_JsonArrayConstructor:
|
||||
{
|
||||
JsonArrayConstructor *jac = (JsonArrayConstructor *) node;
|
||||
|
||||
if (walker(jac->output, context))
|
||||
return true;
|
||||
if (walker(jac->exprs, context))
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case T_JsonAggConstructor:
|
||||
{
|
||||
JsonAggConstructor *ctor = (JsonAggConstructor *) node;
|
||||
|
||||
if (walker(ctor->output, context))
|
||||
return true;
|
||||
if (walker(ctor->agg_order, context))
|
||||
return true;
|
||||
if (walker(ctor->agg_filter, context))
|
||||
return true;
|
||||
if (walker(ctor->over, context))
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case T_JsonObjectAgg:
|
||||
{
|
||||
JsonObjectAgg *joa = (JsonObjectAgg *) node;
|
||||
|
||||
if (walker(joa->constructor, context))
|
||||
return true;
|
||||
if (walker(joa->arg, context))
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case T_JsonArrayAgg:
|
||||
{
|
||||
JsonArrayAgg *jaa = (JsonArrayAgg *) node;
|
||||
|
||||
if (walker(jaa->constructor, context))
|
||||
return true;
|
||||
if (walker(jaa->arg, context))
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case T_JsonArrayQueryConstructor:
|
||||
{
|
||||
JsonArrayQueryConstructor *jaqc = (JsonArrayQueryConstructor *) node;
|
||||
|
||||
if (walker(jaqc->output, context))
|
||||
return true;
|
||||
if (walker(jaqc->query, context))
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
elog(ERROR, "unrecognized node type: %d",
|
||||
(int) nodeTag(node));
|
||||
|
@ -1781,6 +1781,21 @@ _outJsonValueExpr(StringInfo str, const JsonValueExpr *node)
|
||||
WRITE_NODE_FIELD(format);
|
||||
}
|
||||
|
||||
static void
|
||||
_outJsonConstructorExpr(StringInfo str, const JsonConstructorExpr *node)
|
||||
{
|
||||
WRITE_NODE_TYPE("JSONCTOREXPR");
|
||||
|
||||
WRITE_NODE_FIELD(args);
|
||||
WRITE_NODE_FIELD(func);
|
||||
WRITE_NODE_FIELD(coercion);
|
||||
WRITE_INT_FIELD(type);
|
||||
WRITE_NODE_FIELD(returning);
|
||||
WRITE_BOOL_FIELD(unique);
|
||||
WRITE_BOOL_FIELD(absent_on_null);
|
||||
WRITE_LOCATION_FIELD(location);
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* Stuff from pathnodes.h.
|
||||
@ -4576,6 +4591,9 @@ outNode(StringInfo str, const void *obj)
|
||||
case T_JsonValueExpr:
|
||||
_outJsonValueExpr(str, obj);
|
||||
break;
|
||||
case T_JsonConstructorExpr:
|
||||
_outJsonConstructorExpr(str, obj);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
|
@ -1434,6 +1434,26 @@ _readJsonValueExpr(void)
|
||||
READ_DONE();
|
||||
}
|
||||
|
||||
/*
|
||||
* _readJsonConstructorExpr
|
||||
*/
|
||||
static JsonConstructorExpr *
|
||||
_readJsonConstructorExpr(void)
|
||||
{
|
||||
READ_LOCALS(JsonConstructorExpr);
|
||||
|
||||
READ_NODE_FIELD(args);
|
||||
READ_NODE_FIELD(func);
|
||||
READ_NODE_FIELD(coercion);
|
||||
READ_INT_FIELD(type);
|
||||
READ_NODE_FIELD(returning);
|
||||
READ_BOOL_FIELD(unique);
|
||||
READ_BOOL_FIELD(absent_on_null);
|
||||
READ_LOCATION_FIELD(location);
|
||||
|
||||
READ_DONE();
|
||||
}
|
||||
|
||||
/*
|
||||
* Stuff from pathnodes.h.
|
||||
*
|
||||
@ -3025,6 +3045,8 @@ parseNodeString(void)
|
||||
return_value = _readJsonReturning();
|
||||
else if (MATCH("JSONVALUEEXPR", 13))
|
||||
return_value = _readJsonValueExpr();
|
||||
else if (MATCH("JSONCTOREXPR", 12))
|
||||
return_value = _readJsonConstructorExpr();
|
||||
else
|
||||
{
|
||||
elog(ERROR, "badly formatted node string \"%.32s\"...", token);
|
||||
|
@ -50,6 +50,8 @@
|
||||
#include "utils/builtins.h"
|
||||
#include "utils/datum.h"
|
||||
#include "utils/fmgroids.h"
|
||||
#include "utils/json.h"
|
||||
#include "utils/jsonb.h"
|
||||
#include "utils/lsyscache.h"
|
||||
#include "utils/memutils.h"
|
||||
#include "utils/syscache.h"
|
||||
@ -382,6 +384,27 @@ contain_mutable_functions_walker(Node *node, void *context)
|
||||
context))
|
||||
return true;
|
||||
|
||||
if (IsA(node, JsonConstructorExpr))
|
||||
{
|
||||
const JsonConstructorExpr *ctor = (JsonConstructorExpr *) node;
|
||||
ListCell *lc;
|
||||
bool is_jsonb =
|
||||
ctor->returning->format->format_type == JS_FORMAT_JSONB;
|
||||
|
||||
/* Check argument_type => json[b] conversions */
|
||||
foreach(lc, ctor->args)
|
||||
{
|
||||
Oid typid = exprType(lfirst(lc));
|
||||
|
||||
if (is_jsonb ?
|
||||
!to_jsonb_is_immutable(typid) :
|
||||
!to_json_is_immutable(typid))
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Check all subnodes */
|
||||
}
|
||||
|
||||
if (IsA(node, SQLValueFunction))
|
||||
{
|
||||
/* all variants of SQLValueFunction are stable */
|
||||
|
@ -639,11 +639,31 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
|
||||
%type <node> json_format_clause_opt
|
||||
json_representation
|
||||
json_value_expr
|
||||
json_func_expr
|
||||
json_output_clause_opt
|
||||
json_value_constructor
|
||||
json_object_constructor
|
||||
json_object_constructor_args
|
||||
json_object_constructor_args_opt
|
||||
json_object_args
|
||||
json_object_func_args
|
||||
json_array_constructor
|
||||
json_name_and_value
|
||||
json_aggregate_func
|
||||
json_object_aggregate_constructor
|
||||
json_array_aggregate_constructor
|
||||
|
||||
%type <list> json_name_and_value_list
|
||||
json_value_expr_list
|
||||
json_array_aggregate_order_by_clause_opt
|
||||
|
||||
%type <ival> json_encoding
|
||||
json_encoding_clause_opt
|
||||
|
||||
%type <boolean> json_key_uniqueness_constraint_opt
|
||||
json_object_constructor_null_clause_opt
|
||||
json_array_constructor_null_clause_opt
|
||||
|
||||
/*
|
||||
* Non-keyword token types. These are hard-wired into the "flex" lexer.
|
||||
* They must be listed first so that their numeric codes do not depend on
|
||||
@ -669,7 +689,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
|
||||
*/
|
||||
|
||||
/* ordinary key words in alphabetical order */
|
||||
%token <keyword> ABORT_P ABSOLUTE_P ACCESS ACTION ADD_P ADMIN AFTER
|
||||
%token <keyword> ABORT_P ABSENT ABSOLUTE_P ACCESS ACTION ADD_P ADMIN AFTER
|
||||
AGGREGATE ALL ALSO ALTER ALWAYS ANALYSE ANALYZE AND ANY ARRAY AS ASC
|
||||
ASENSITIVE ASSERTION ASSIGNMENT ASYMMETRIC ATOMIC AT ATTACH ATTRIBUTE AUTHORIZATION
|
||||
|
||||
@ -706,9 +726,9 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
|
||||
INNER_P INOUT INPUT_P INSENSITIVE INSERT INSTEAD INT_P INTEGER
|
||||
INTERSECT INTERVAL INTO INVOKER IS ISNULL ISOLATION
|
||||
|
||||
JOIN JSON
|
||||
JOIN JSON JSON_ARRAY JSON_ARRAYAGG JSON_OBJECT JSON_OBJECTAGG
|
||||
|
||||
KEY
|
||||
KEY KEYS
|
||||
|
||||
LABEL LANGUAGE LARGE_P LAST_P LATERAL_P
|
||||
LEADING LEAKPROOF LEAST LEFT LEVEL LIKE LIMIT LISTEN LOAD LOCAL
|
||||
@ -772,7 +792,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
|
||||
* as NOT, at least with respect to their left-hand subexpression.
|
||||
* NULLS_LA and WITH_LA are needed to make the grammar LALR(1).
|
||||
*/
|
||||
%token NOT_LA NULLS_LA WITH_LA
|
||||
%token NOT_LA NULLS_LA WITH_LA WITH_LA_UNIQUE WITHOUT_LA
|
||||
|
||||
/*
|
||||
* The grammar likewise thinks these tokens are keywords, but they are never
|
||||
@ -826,11 +846,13 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
|
||||
* Using the same precedence as IDENT seems right for the reasons given above.
|
||||
*/
|
||||
%nonassoc UNBOUNDED /* ideally would have same precedence as IDENT */
|
||||
%nonassoc ABSENT UNIQUE
|
||||
%nonassoc IDENT PARTITION RANGE ROWS GROUPS PRECEDING FOLLOWING CUBE ROLLUP
|
||||
%left Op OPERATOR /* multi-character ops and user-defined operators */
|
||||
%left '+' '-'
|
||||
%left '*' '/' '%'
|
||||
%left '^'
|
||||
%left KEYS /* UNIQUE [ KEYS ] */
|
||||
/* Unary Operators */
|
||||
%left AT /* sets precedence for AT TIME ZONE */
|
||||
%left COLLATE
|
||||
@ -848,6 +870,9 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
|
||||
*/
|
||||
%left JOIN CROSS LEFT FULL RIGHT INNER_P NATURAL
|
||||
|
||||
%nonassoc empty_json_unique
|
||||
%left WITHOUT WITH_LA_UNIQUE
|
||||
|
||||
%%
|
||||
|
||||
/*
|
||||
@ -13415,7 +13440,7 @@ ConstInterval:
|
||||
|
||||
opt_timezone:
|
||||
WITH_LA TIME ZONE { $$ = true; }
|
||||
| WITHOUT TIME ZONE { $$ = false; }
|
||||
| WITHOUT_LA TIME ZONE { $$ = false; }
|
||||
| /*EMPTY*/ { $$ = false; }
|
||||
;
|
||||
|
||||
@ -14028,6 +14053,17 @@ b_expr: c_expr
|
||||
}
|
||||
;
|
||||
|
||||
json_key_uniqueness_constraint_opt:
|
||||
WITH_LA_UNIQUE unique_keys { $$ = true; }
|
||||
| WITHOUT unique_keys { $$ = false; }
|
||||
| /* EMPTY */ %prec empty_json_unique { $$ = false; }
|
||||
;
|
||||
|
||||
unique_keys:
|
||||
UNIQUE
|
||||
| UNIQUE KEYS
|
||||
;
|
||||
|
||||
/*
|
||||
* Productions that can be used in both a_expr and b_expr.
|
||||
*
|
||||
@ -14280,6 +14316,15 @@ func_expr: func_application within_group_clause filter_clause over_clause
|
||||
n->over = $4;
|
||||
$$ = (Node *) n;
|
||||
}
|
||||
| json_aggregate_func filter_clause over_clause
|
||||
{
|
||||
JsonAggConstructor *n = IsA($1, JsonObjectAgg) ?
|
||||
((JsonObjectAgg *) $1)->constructor :
|
||||
((JsonArrayAgg *) $1)->constructor;
|
||||
n->agg_filter = $2;
|
||||
n->over = $3;
|
||||
$$ = (Node *) $1;
|
||||
}
|
||||
| func_expr_common_subexpr
|
||||
{ $$ = $1; }
|
||||
;
|
||||
@ -14293,6 +14338,7 @@ func_expr: func_application within_group_clause filter_clause over_clause
|
||||
func_expr_windowless:
|
||||
func_application { $$ = $1; }
|
||||
| func_expr_common_subexpr { $$ = $1; }
|
||||
| json_aggregate_func { $$ = $1; }
|
||||
;
|
||||
|
||||
/*
|
||||
@ -14580,6 +14626,8 @@ func_expr_common_subexpr:
|
||||
n->location = @1;
|
||||
$$ = (Node *)n;
|
||||
}
|
||||
| json_func_expr
|
||||
{ $$ = $1; }
|
||||
;
|
||||
|
||||
/*
|
||||
@ -15279,11 +15327,14 @@ opt_asymmetric: ASYMMETRIC
|
||||
;
|
||||
|
||||
/* SQL/JSON support */
|
||||
json_func_expr:
|
||||
json_value_constructor
|
||||
;
|
||||
|
||||
json_value_expr:
|
||||
a_expr json_format_clause_opt
|
||||
{
|
||||
$$ = (Node *) makeJsonValueExpr((Expr *) $1, $2);
|
||||
$$ = (Node *) makeJsonValueExpr((Expr *) $1, castNode(JsonFormat, $2));
|
||||
}
|
||||
;
|
||||
|
||||
@ -15291,7 +15342,7 @@ json_format_clause_opt:
|
||||
FORMAT json_representation
|
||||
{
|
||||
$$ = $2;
|
||||
$$.location = @1;
|
||||
castNode(JsonFormat, $$)->location = @1;
|
||||
}
|
||||
| /* EMPTY */
|
||||
{
|
||||
@ -15321,12 +15372,198 @@ json_output_clause_opt:
|
||||
{
|
||||
JsonOutput *n = makeNode(JsonOutput);
|
||||
n->typeName = $2;
|
||||
n->returning.format = $3;
|
||||
n->returning = makeNode(JsonReturning);
|
||||
n->returning->format = (JsonFormat *) $3;
|
||||
$$ = (Node *) n;
|
||||
}
|
||||
| /* EMPTY */ { $$ = NULL; }
|
||||
;
|
||||
|
||||
json_value_constructor:
|
||||
json_object_constructor
|
||||
| json_array_constructor
|
||||
;
|
||||
|
||||
json_object_constructor:
|
||||
JSON_OBJECT '(' json_object_args ')'
|
||||
{
|
||||
$$ = $3;
|
||||
}
|
||||
;
|
||||
|
||||
json_object_args:
|
||||
json_object_constructor_args
|
||||
| json_object_func_args
|
||||
;
|
||||
|
||||
json_object_func_args:
|
||||
func_arg_list
|
||||
{
|
||||
List *func = list_make1(makeString("json_object"));
|
||||
$$ = (Node *) makeFuncCall(func, $1, COERCE_EXPLICIT_CALL, @1);
|
||||
}
|
||||
;
|
||||
|
||||
json_object_constructor_args:
|
||||
json_object_constructor_args_opt json_output_clause_opt
|
||||
{
|
||||
JsonObjectConstructor *n = (JsonObjectConstructor *) $1;
|
||||
n->output = (JsonOutput *) $2;
|
||||
n->location = @1;
|
||||
$$ = (Node *) n;
|
||||
}
|
||||
;
|
||||
|
||||
json_object_constructor_args_opt:
|
||||
json_name_and_value_list
|
||||
json_object_constructor_null_clause_opt
|
||||
json_key_uniqueness_constraint_opt
|
||||
{
|
||||
JsonObjectConstructor *n = makeNode(JsonObjectConstructor);
|
||||
n->exprs = $1;
|
||||
n->absent_on_null = $2;
|
||||
n->unique = $3;
|
||||
$$ = (Node *) n;
|
||||
}
|
||||
| /* EMPTY */
|
||||
{
|
||||
JsonObjectConstructor *n = makeNode(JsonObjectConstructor);
|
||||
n->exprs = NULL;
|
||||
n->absent_on_null = false;
|
||||
n->unique = false;
|
||||
$$ = (Node *) n;
|
||||
}
|
||||
;
|
||||
|
||||
json_name_and_value_list:
|
||||
json_name_and_value
|
||||
{ $$ = list_make1($1); }
|
||||
| json_name_and_value_list ',' json_name_and_value
|
||||
{ $$ = lappend($1, $3); }
|
||||
;
|
||||
|
||||
json_name_and_value:
|
||||
/* TODO This is not supported due to conflicts
|
||||
KEY c_expr VALUE_P json_value_expr %prec POSTFIXOP
|
||||
{ $$ = makeJsonKeyValue($2, $4); }
|
||||
|
|
||||
*/
|
||||
c_expr VALUE_P json_value_expr
|
||||
{ $$ = makeJsonKeyValue($1, $3); }
|
||||
|
|
||||
a_expr ':' json_value_expr
|
||||
{ $$ = makeJsonKeyValue($1, $3); }
|
||||
;
|
||||
|
||||
json_object_constructor_null_clause_opt:
|
||||
NULL_P ON NULL_P { $$ = false; }
|
||||
| ABSENT ON NULL_P { $$ = true; }
|
||||
| /* EMPTY */ { $$ = false; }
|
||||
;
|
||||
|
||||
json_array_constructor:
|
||||
JSON_ARRAY '('
|
||||
json_value_expr_list
|
||||
json_array_constructor_null_clause_opt
|
||||
json_output_clause_opt
|
||||
')'
|
||||
{
|
||||
JsonArrayConstructor *n = makeNode(JsonArrayConstructor);
|
||||
n->exprs = $3;
|
||||
n->absent_on_null = $4;
|
||||
n->output = (JsonOutput *) $5;
|
||||
n->location = @1;
|
||||
$$ = (Node *) n;
|
||||
}
|
||||
| JSON_ARRAY '('
|
||||
select_no_parens
|
||||
/* json_format_clause_opt */
|
||||
/* json_array_constructor_null_clause_opt */
|
||||
json_output_clause_opt
|
||||
')'
|
||||
{
|
||||
JsonArrayQueryConstructor *n = makeNode(JsonArrayQueryConstructor);
|
||||
n->query = $3;
|
||||
n->format = makeJsonFormat(JS_FORMAT_DEFAULT, JS_ENC_DEFAULT, -1);
|
||||
/* n->format = $4; */
|
||||
n->absent_on_null = true /* $5 */;
|
||||
n->output = (JsonOutput *) $4;
|
||||
n->location = @1;
|
||||
$$ = (Node *) n;
|
||||
}
|
||||
| JSON_ARRAY '('
|
||||
json_output_clause_opt
|
||||
')'
|
||||
{
|
||||
JsonArrayConstructor *n = makeNode(JsonArrayConstructor);
|
||||
n->exprs = NIL;
|
||||
n->absent_on_null = true;
|
||||
n->output = (JsonOutput *) $3;
|
||||
n->location = @1;
|
||||
$$ = (Node *) n;
|
||||
}
|
||||
;
|
||||
|
||||
json_value_expr_list:
|
||||
json_value_expr { $$ = list_make1($1); }
|
||||
| json_value_expr_list ',' json_value_expr { $$ = lappend($1, $3);}
|
||||
;
|
||||
|
||||
json_array_constructor_null_clause_opt:
|
||||
NULL_P ON NULL_P { $$ = false; }
|
||||
| ABSENT ON NULL_P { $$ = true; }
|
||||
| /* EMPTY */ { $$ = true; }
|
||||
;
|
||||
|
||||
json_aggregate_func:
|
||||
json_object_aggregate_constructor
|
||||
| json_array_aggregate_constructor
|
||||
;
|
||||
|
||||
json_object_aggregate_constructor:
|
||||
JSON_OBJECTAGG '('
|
||||
json_name_and_value
|
||||
json_object_constructor_null_clause_opt
|
||||
json_key_uniqueness_constraint_opt
|
||||
json_output_clause_opt
|
||||
')'
|
||||
{
|
||||
JsonObjectAgg *n = makeNode(JsonObjectAgg);
|
||||
n->arg = (JsonKeyValue *) $3;
|
||||
n->absent_on_null = $4;
|
||||
n->unique = $5;
|
||||
n->constructor = makeNode(JsonAggConstructor);
|
||||
n->constructor->output = (JsonOutput *) $6;
|
||||
n->constructor->agg_order = NULL;
|
||||
n->constructor->location = @1;
|
||||
$$ = (Node *) n;
|
||||
}
|
||||
;
|
||||
|
||||
json_array_aggregate_constructor:
|
||||
JSON_ARRAYAGG '('
|
||||
json_value_expr
|
||||
json_array_aggregate_order_by_clause_opt
|
||||
json_array_constructor_null_clause_opt
|
||||
json_output_clause_opt
|
||||
')'
|
||||
{
|
||||
JsonArrayAgg *n = makeNode(JsonArrayAgg);
|
||||
n->arg = (JsonValueExpr *) $3;
|
||||
n->absent_on_null = $5;
|
||||
n->constructor = makeNode(JsonAggConstructor);
|
||||
n->constructor->agg_order = $4;
|
||||
n->constructor->output = (JsonOutput *) $6;
|
||||
n->constructor->location = @1;
|
||||
$$ = (Node *) n;
|
||||
}
|
||||
;
|
||||
|
||||
json_array_aggregate_order_by_clause_opt:
|
||||
ORDER BY sortby_list { $$ = $3; }
|
||||
| /* EMPTY */ { $$ = NIL; }
|
||||
;
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* target list for SELECT
|
||||
@ -15771,6 +16008,7 @@ BareColLabel: IDENT { $$ = $1; }
|
||||
*/
|
||||
unreserved_keyword:
|
||||
ABORT_P
|
||||
| ABSENT
|
||||
| ABSOLUTE_P
|
||||
| ACCESS
|
||||
| ACTION
|
||||
@ -15901,6 +16139,7 @@ unreserved_keyword:
|
||||
| ISOLATION
|
||||
| JSON
|
||||
| KEY
|
||||
| KEYS
|
||||
| LABEL
|
||||
| LANGUAGE
|
||||
| LARGE_P
|
||||
@ -16109,6 +16348,10 @@ col_name_keyword:
|
||||
| INT_P
|
||||
| INTEGER
|
||||
| INTERVAL
|
||||
| JSON_ARRAY
|
||||
| JSON_ARRAYAGG
|
||||
| JSON_OBJECT
|
||||
| JSON_OBJECTAGG
|
||||
| LEAST
|
||||
| NATIONAL
|
||||
| NCHAR
|
||||
@ -16277,6 +16520,7 @@ reserved_keyword:
|
||||
*/
|
||||
bare_label_keyword:
|
||||
ABORT_P
|
||||
| ABSENT
|
||||
| ABSOLUTE_P
|
||||
| ACCESS
|
||||
| ACTION
|
||||
@ -16462,7 +16706,12 @@ bare_label_keyword:
|
||||
| ISOLATION
|
||||
| JOIN
|
||||
| JSON
|
||||
| JSON_ARRAY
|
||||
| JSON_ARRAYAGG
|
||||
| JSON_OBJECT
|
||||
| JSON_OBJECTAGG
|
||||
| KEY
|
||||
| KEYS
|
||||
| LABEL
|
||||
| LANGUAGE
|
||||
| LARGE_P
|
||||
|
@ -15,6 +15,8 @@
|
||||
|
||||
#include "postgres.h"
|
||||
|
||||
#include "catalog/pg_aggregate.h"
|
||||
#include "catalog/pg_proc.h"
|
||||
#include "catalog/pg_type.h"
|
||||
#include "commands/dbcommands.h"
|
||||
#include "miscadmin.h"
|
||||
@ -75,6 +77,14 @@ static Node *transformWholeRowRef(ParseState *pstate,
|
||||
static Node *transformIndirection(ParseState *pstate, A_Indirection *ind);
|
||||
static Node *transformTypeCast(ParseState *pstate, TypeCast *tc);
|
||||
static Node *transformCollateClause(ParseState *pstate, CollateClause *c);
|
||||
static Node *transformJsonObjectConstructor(ParseState *pstate,
|
||||
JsonObjectConstructor *ctor);
|
||||
static Node *transformJsonArrayConstructor(ParseState *pstate,
|
||||
JsonArrayConstructor *ctor);
|
||||
static Node *transformJsonArrayQueryConstructor(ParseState *pstate,
|
||||
JsonArrayQueryConstructor *ctor);
|
||||
static Node *transformJsonObjectAgg(ParseState *pstate, JsonObjectAgg *agg);
|
||||
static Node *transformJsonArrayAgg(ParseState *pstate, JsonArrayAgg *agg);
|
||||
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,
|
||||
@ -302,6 +312,26 @@ transformExprRecurse(ParseState *pstate, Node *expr)
|
||||
break;
|
||||
}
|
||||
|
||||
case T_JsonObjectConstructor:
|
||||
result = transformJsonObjectConstructor(pstate, (JsonObjectConstructor *) expr);
|
||||
break;
|
||||
|
||||
case T_JsonArrayConstructor:
|
||||
result = transformJsonArrayConstructor(pstate, (JsonArrayConstructor *) expr);
|
||||
break;
|
||||
|
||||
case T_JsonArrayQueryConstructor:
|
||||
result = transformJsonArrayQueryConstructor(pstate, (JsonArrayQueryConstructor *) expr);
|
||||
break;
|
||||
|
||||
case T_JsonObjectAgg:
|
||||
result = transformJsonObjectAgg(pstate, (JsonObjectAgg *) expr);
|
||||
break;
|
||||
|
||||
case T_JsonArrayAgg:
|
||||
result = transformJsonArrayAgg(pstate, (JsonArrayAgg *) expr);
|
||||
break;
|
||||
|
||||
default:
|
||||
/* should not reach here */
|
||||
elog(ERROR, "unrecognized node type: %d", (int) nodeTag(expr));
|
||||
@ -3280,3 +3310,562 @@ transformJsonValueExpr(ParseState *pstate, JsonValueExpr *ve,
|
||||
|
||||
return expr;
|
||||
}
|
||||
|
||||
/*
|
||||
* Checks specified output format for its applicability to the target type.
|
||||
*/
|
||||
static void
|
||||
checkJsonOutputFormat(ParseState *pstate, const JsonFormat *format,
|
||||
Oid targettype, bool allow_format_for_non_strings)
|
||||
{
|
||||
if (!allow_format_for_non_strings &&
|
||||
format->format_type != JS_FORMAT_DEFAULT &&
|
||||
(targettype != BYTEAOID &&
|
||||
targettype != JSONOID &&
|
||||
targettype != JSONBOID))
|
||||
{
|
||||
char typcategory;
|
||||
bool typispreferred;
|
||||
|
||||
get_type_category_preferred(targettype, &typcategory, &typispreferred);
|
||||
|
||||
if (typcategory != TYPCATEGORY_STRING)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
parser_errposition(pstate, format->location),
|
||||
errmsg("cannot use JSON format with non-string output types")));
|
||||
}
|
||||
|
||||
if (format->format_type == JS_FORMAT_JSON)
|
||||
{
|
||||
JsonEncoding enc = format->encoding != JS_ENC_DEFAULT ?
|
||||
format->encoding : JS_ENC_UTF8;
|
||||
|
||||
if (targettype != BYTEAOID &&
|
||||
format->encoding != JS_ENC_DEFAULT)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
parser_errposition(pstate, format->location),
|
||||
errmsg("cannot set JSON encoding for non-bytea output types")));
|
||||
|
||||
if (enc != JS_ENC_UTF8)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("unsupported JSON encoding"),
|
||||
errhint("only UTF8 JSON encoding is supported"),
|
||||
parser_errposition(pstate, format->location)));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Transform JSON output clause.
|
||||
*
|
||||
* Assigns target type oid and modifier.
|
||||
* Assigns default format or checks specified format for its applicability to
|
||||
* the target type.
|
||||
*/
|
||||
static JsonReturning *
|
||||
transformJsonOutput(ParseState *pstate, const JsonOutput *output,
|
||||
bool allow_format)
|
||||
{
|
||||
JsonReturning *ret;
|
||||
|
||||
/* if output clause is not specified, make default clause value */
|
||||
if (!output)
|
||||
{
|
||||
ret = makeNode(JsonReturning);
|
||||
|
||||
ret->format = makeJsonFormat(JS_FORMAT_DEFAULT, JS_ENC_DEFAULT, -1);
|
||||
ret->typid = InvalidOid;
|
||||
ret->typmod = -1;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = copyObject(output->returning);
|
||||
|
||||
typenameTypeIdAndMod(pstate, output->typeName, &ret->typid, &ret->typmod);
|
||||
|
||||
if (output->typeName->setof)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("returning SETOF types is not supported in SQL/JSON functions")));
|
||||
|
||||
if (ret->format->format_type == JS_FORMAT_DEFAULT)
|
||||
/* assign JSONB format when returning jsonb, or JSON format otherwise */
|
||||
ret->format->format_type =
|
||||
ret->typid == JSONBOID ? JS_FORMAT_JSONB : JS_FORMAT_JSON;
|
||||
else
|
||||
checkJsonOutputFormat(pstate, ret->format, ret->typid, allow_format);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Transform JSON output clause of JSON contructor functions.
|
||||
*
|
||||
* Derive RETURNING type, if not specified, from argument types.
|
||||
*/
|
||||
static JsonReturning *
|
||||
transformJsonConstructorOutput(ParseState *pstate, JsonOutput *output,
|
||||
List *args)
|
||||
{
|
||||
JsonReturning *returning = transformJsonOutput(pstate, output, true);
|
||||
|
||||
if (!OidIsValid(returning->typid))
|
||||
{
|
||||
ListCell *lc;
|
||||
bool have_json = false;
|
||||
bool have_jsonb = false;
|
||||
|
||||
foreach(lc, args)
|
||||
{
|
||||
Node *expr = lfirst(lc);
|
||||
Oid typid = exprType(expr);
|
||||
|
||||
have_json |= typid == JSONOID;
|
||||
have_jsonb |= typid == JSONBOID;
|
||||
|
||||
if (have_jsonb)
|
||||
break;
|
||||
}
|
||||
|
||||
if (have_jsonb)
|
||||
{
|
||||
returning->typid = JSONBOID;
|
||||
returning->format->format_type = JS_FORMAT_JSONB;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Note: this includes the have_json case */
|
||||
|
||||
/* XXX TEXT is default by the standard, but we return JSON */
|
||||
returning->typid = JSONOID;
|
||||
returning->format->format_type = JS_FORMAT_JSON;
|
||||
}
|
||||
|
||||
returning->typmod = -1;
|
||||
}
|
||||
|
||||
return returning;
|
||||
}
|
||||
|
||||
/*
|
||||
* Coerce json[b]-valued function expression to the output type.
|
||||
*/
|
||||
static Node *
|
||||
coerceJsonFuncExpr(ParseState *pstate, Node *expr,
|
||||
const JsonReturning *returning, bool report_error)
|
||||
{
|
||||
Node *res;
|
||||
int location;
|
||||
Oid exprtype = exprType(expr);
|
||||
|
||||
/* if output type is not specified or equals to function type, return */
|
||||
if (!OidIsValid(returning->typid) || returning->typid == exprtype)
|
||||
return expr;
|
||||
|
||||
location = exprLocation(expr);
|
||||
|
||||
if (location < 0)
|
||||
location = returning ? returning->format->location : -1;
|
||||
|
||||
/* special case for RETURNING bytea FORMAT json */
|
||||
if (returning->format->format_type == JS_FORMAT_JSON &&
|
||||
returning->typid == BYTEAOID)
|
||||
{
|
||||
/* encode json text into bytea using pg_convert_to() */
|
||||
Node *texpr = coerce_to_specific_type(pstate, expr, TEXTOID,
|
||||
"JSON_FUNCTION");
|
||||
Const *enc = getJsonEncodingConst(returning->format);
|
||||
FuncExpr *fexpr = makeFuncExpr(F_CONVERT_TO, BYTEAOID,
|
||||
list_make2(texpr, enc),
|
||||
InvalidOid, InvalidOid,
|
||||
COERCE_EXPLICIT_CALL);
|
||||
fexpr->location = location;
|
||||
|
||||
return (Node *) fexpr;
|
||||
}
|
||||
|
||||
/* try to coerce expression to the output type */
|
||||
res = coerce_to_target_type(pstate, expr, exprtype,
|
||||
returning->typid, returning->typmod,
|
||||
/* XXX throwing errors when casting to char(N) */
|
||||
COERCION_EXPLICIT,
|
||||
COERCE_EXPLICIT_CAST,
|
||||
location);
|
||||
|
||||
if (!res && report_error)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_CANNOT_COERCE),
|
||||
errmsg("cannot cast type %s to %s",
|
||||
format_type_be(exprtype),
|
||||
format_type_be(returning->typid)),
|
||||
parser_coercion_errposition(pstate, location, expr)));
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static Node *
|
||||
makeJsonConstructorExpr(ParseState *pstate, JsonConstructorType type,
|
||||
List *args, Expr *fexpr, JsonReturning *returning,
|
||||
bool unique, bool absent_on_null, int location)
|
||||
{
|
||||
JsonConstructorExpr *jsctor = makeNode(JsonConstructorExpr);
|
||||
Node *placeholder;
|
||||
Node *coercion;
|
||||
Oid intermediate_typid =
|
||||
returning->format->format_type == JS_FORMAT_JSONB ? JSONBOID : JSONOID;
|
||||
|
||||
jsctor->args = args;
|
||||
jsctor->func = fexpr;
|
||||
jsctor->type = type;
|
||||
jsctor->returning = returning;
|
||||
jsctor->unique = unique;
|
||||
jsctor->absent_on_null = absent_on_null;
|
||||
jsctor->location = location;
|
||||
|
||||
if (fexpr)
|
||||
placeholder = makeCaseTestExpr((Node *) fexpr);
|
||||
else
|
||||
{
|
||||
CaseTestExpr *cte = makeNode(CaseTestExpr);
|
||||
|
||||
cte->typeId = intermediate_typid;
|
||||
cte->typeMod = -1;
|
||||
cte->collation = InvalidOid;
|
||||
|
||||
placeholder = (Node *) cte;
|
||||
}
|
||||
|
||||
coercion = coerceJsonFuncExpr(pstate, placeholder, returning, true);
|
||||
|
||||
if (coercion != placeholder)
|
||||
jsctor->coercion = (Expr *) coercion;
|
||||
|
||||
return (Node *) jsctor;
|
||||
}
|
||||
|
||||
/*
|
||||
* Transform JSON_OBJECT() constructor.
|
||||
*
|
||||
* JSON_OBJECT() is transformed into json[b]_build_object[_ext]() call
|
||||
* depending on the output JSON format. The first two arguments of
|
||||
* json[b]_build_object_ext() are absent_on_null and check_key_uniqueness.
|
||||
*
|
||||
* Then function call result is coerced to the target type.
|
||||
*/
|
||||
static Node *
|
||||
transformJsonObjectConstructor(ParseState *pstate, JsonObjectConstructor *ctor)
|
||||
{
|
||||
JsonReturning *returning;
|
||||
List *args = NIL;
|
||||
|
||||
/* transform key-value pairs, if any */
|
||||
if (ctor->exprs)
|
||||
{
|
||||
ListCell *lc;
|
||||
|
||||
/* transform and append key-value arguments */
|
||||
foreach(lc, ctor->exprs)
|
||||
{
|
||||
JsonKeyValue *kv = castNode(JsonKeyValue, lfirst(lc));
|
||||
Node *key = transformExprRecurse(pstate, (Node *) kv->key);
|
||||
Node *val = transformJsonValueExpr(pstate, kv->value,
|
||||
JS_FORMAT_DEFAULT);
|
||||
|
||||
args = lappend(args, key);
|
||||
args = lappend(args, val);
|
||||
}
|
||||
}
|
||||
|
||||
returning = transformJsonConstructorOutput(pstate, ctor->output, args);
|
||||
|
||||
return makeJsonConstructorExpr(pstate, JSCTOR_JSON_OBJECT, args, NULL,
|
||||
returning, ctor->unique,
|
||||
ctor->absent_on_null, ctor->location);
|
||||
}
|
||||
|
||||
/*
|
||||
* Transform JSON_ARRAY(query [FORMAT] [RETURNING] [ON NULL]) into
|
||||
* (SELECT JSON_ARRAYAGG(a [FORMAT] [RETURNING] [ON NULL]) FROM (query) q(a))
|
||||
*/
|
||||
static Node *
|
||||
transformJsonArrayQueryConstructor(ParseState *pstate,
|
||||
JsonArrayQueryConstructor *ctor)
|
||||
{
|
||||
SubLink *sublink = makeNode(SubLink);
|
||||
SelectStmt *select = makeNode(SelectStmt);
|
||||
RangeSubselect *range = makeNode(RangeSubselect);
|
||||
Alias *alias = makeNode(Alias);
|
||||
ResTarget *target = makeNode(ResTarget);
|
||||
JsonArrayAgg *agg = makeNode(JsonArrayAgg);
|
||||
ColumnRef *colref = makeNode(ColumnRef);
|
||||
Query *query;
|
||||
ParseState *qpstate;
|
||||
|
||||
/* Transform query only for counting target list entries. */
|
||||
qpstate = make_parsestate(pstate);
|
||||
|
||||
query = transformStmt(qpstate, ctor->query);
|
||||
|
||||
if (count_nonjunk_tlist_entries(query->targetList) != 1)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||
errmsg("subquery must return only one column"),
|
||||
parser_errposition(pstate, ctor->location)));
|
||||
|
||||
free_parsestate(qpstate);
|
||||
|
||||
colref->fields = list_make2(makeString(pstrdup("q")),
|
||||
makeString(pstrdup("a")));
|
||||
colref->location = ctor->location;
|
||||
|
||||
agg->arg = makeJsonValueExpr((Expr *) colref, ctor->format);
|
||||
agg->absent_on_null = ctor->absent_on_null;
|
||||
agg->constructor = makeNode(JsonAggConstructor);
|
||||
agg->constructor->agg_order = NIL;
|
||||
agg->constructor->output = ctor->output;
|
||||
agg->constructor->location = ctor->location;
|
||||
|
||||
target->name = NULL;
|
||||
target->indirection = NIL;
|
||||
target->val = (Node *) agg;
|
||||
target->location = ctor->location;
|
||||
|
||||
alias->aliasname = pstrdup("q");
|
||||
alias->colnames = list_make1(makeString(pstrdup("a")));
|
||||
|
||||
range->lateral = false;
|
||||
range->subquery = ctor->query;
|
||||
range->alias = alias;
|
||||
|
||||
select->targetList = list_make1(target);
|
||||
select->fromClause = list_make1(range);
|
||||
|
||||
sublink->subLinkType = EXPR_SUBLINK;
|
||||
sublink->subLinkId = 0;
|
||||
sublink->testexpr = NULL;
|
||||
sublink->operName = NIL;
|
||||
sublink->subselect = (Node *) select;
|
||||
sublink->location = ctor->location;
|
||||
|
||||
return transformExprRecurse(pstate, (Node *) sublink);
|
||||
}
|
||||
|
||||
/*
|
||||
* Common code for JSON_OBJECTAGG and JSON_ARRAYAGG transformation.
|
||||
*/
|
||||
static Node *
|
||||
transformJsonAggConstructor(ParseState *pstate, JsonAggConstructor *agg_ctor,
|
||||
JsonReturning *returning, List *args,
|
||||
const char *aggfn, Oid aggtype,
|
||||
JsonConstructorType ctor_type,
|
||||
bool unique, bool absent_on_null)
|
||||
{
|
||||
Oid aggfnoid;
|
||||
Node *node;
|
||||
Expr *aggfilter = agg_ctor->agg_filter ? (Expr *)
|
||||
transformWhereClause(pstate, agg_ctor->agg_filter,
|
||||
EXPR_KIND_FILTER, "FILTER") : NULL;
|
||||
|
||||
aggfnoid = DatumGetInt32(DirectFunctionCall1(regprocin,
|
||||
CStringGetDatum(aggfn)));
|
||||
|
||||
if (agg_ctor->over)
|
||||
{
|
||||
/* window function */
|
||||
WindowFunc *wfunc = makeNode(WindowFunc);
|
||||
|
||||
wfunc->winfnoid = aggfnoid;
|
||||
wfunc->wintype = aggtype;
|
||||
/* wincollid and inputcollid will be set by parse_collate.c */
|
||||
wfunc->args = args;
|
||||
/* winref will be set by transformWindowFuncCall */
|
||||
wfunc->winstar = false;
|
||||
wfunc->winagg = true;
|
||||
wfunc->aggfilter = aggfilter;
|
||||
wfunc->location = agg_ctor->location;
|
||||
|
||||
/*
|
||||
* ordered aggs not allowed in windows yet
|
||||
*/
|
||||
if (agg_ctor->agg_order != NIL)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("aggregate ORDER BY is not implemented for window functions"),
|
||||
parser_errposition(pstate, agg_ctor->location)));
|
||||
|
||||
/* parse_agg.c does additional window-func-specific processing */
|
||||
transformWindowFuncCall(pstate, wfunc, agg_ctor->over);
|
||||
|
||||
node = (Node *) wfunc;
|
||||
}
|
||||
else
|
||||
{
|
||||
Aggref *aggref = makeNode(Aggref);
|
||||
|
||||
aggref->aggfnoid = aggfnoid;
|
||||
aggref->aggtype = aggtype;
|
||||
|
||||
/* aggcollid and inputcollid will be set by parse_collate.c */
|
||||
aggref->aggtranstype = InvalidOid; /* will be set by planner */
|
||||
/* aggargtypes will be set by transformAggregateCall */
|
||||
/* aggdirectargs and args will be set by transformAggregateCall */
|
||||
/* aggorder and aggdistinct will be set by transformAggregateCall */
|
||||
aggref->aggfilter = aggfilter;
|
||||
aggref->aggstar = false;
|
||||
aggref->aggvariadic = false;
|
||||
aggref->aggkind = AGGKIND_NORMAL;
|
||||
/* agglevelsup will be set by transformAggregateCall */
|
||||
aggref->aggsplit = AGGSPLIT_SIMPLE; /* planner might change this */
|
||||
aggref->location = agg_ctor->location;
|
||||
|
||||
transformAggregateCall(pstate, aggref, args, agg_ctor->agg_order, false);
|
||||
|
||||
node = (Node *) aggref;
|
||||
}
|
||||
|
||||
return makeJsonConstructorExpr(pstate, ctor_type, NIL, (Expr *) node,
|
||||
returning, unique, absent_on_null,
|
||||
agg_ctor->location);
|
||||
}
|
||||
|
||||
/*
|
||||
* Transform JSON_OBJECTAGG() aggregate function.
|
||||
*
|
||||
* JSON_OBJECTAGG() is transformed into
|
||||
* json[b]_objectagg(key, value, absent_on_null, check_unique) call depending on
|
||||
* the output JSON format. Then the function call result is coerced to the
|
||||
* target output type.
|
||||
*/
|
||||
static Node *
|
||||
transformJsonObjectAgg(ParseState *pstate, JsonObjectAgg *agg)
|
||||
{
|
||||
JsonReturning *returning;
|
||||
Node *key;
|
||||
Node *val;
|
||||
List *args;
|
||||
const char *aggfnname;
|
||||
Oid aggtype;
|
||||
|
||||
key = transformExprRecurse(pstate, (Node *) agg->arg->key);
|
||||
val = transformJsonValueExpr(pstate, agg->arg->value, JS_FORMAT_DEFAULT);
|
||||
args = list_make2(key, val);
|
||||
|
||||
returning = transformJsonConstructorOutput(pstate, agg->constructor->output,
|
||||
args);
|
||||
|
||||
if (returning->format->format_type == JS_FORMAT_JSONB)
|
||||
{
|
||||
if (agg->absent_on_null)
|
||||
if (agg->unique)
|
||||
aggfnname = "pg_catalog.jsonb_object_agg_unique_strict"; /* F_JSONB_OBJECT_AGG_UNIQUE_STRICT */
|
||||
else
|
||||
aggfnname = "pg_catalog.jsonb_object_agg_strict"; /* F_JSONB_OBJECT_AGG_STRICT */
|
||||
else
|
||||
if (agg->unique)
|
||||
aggfnname = "pg_catalog.jsonb_object_agg_unique"; /* F_JSONB_OBJECT_AGG_UNIQUE */
|
||||
else
|
||||
aggfnname = "pg_catalog.jsonb_object_agg"; /* F_JSONB_OBJECT_AGG */
|
||||
|
||||
aggtype = JSONBOID;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (agg->absent_on_null)
|
||||
if (agg->unique)
|
||||
aggfnname = "pg_catalog.json_object_agg_unique_strict"; /* F_JSON_OBJECT_AGG_UNIQUE_STRICT */
|
||||
else
|
||||
aggfnname = "pg_catalog.json_object_agg_strict"; /* F_JSON_OBJECT_AGG_STRICT */
|
||||
else
|
||||
if (agg->unique)
|
||||
aggfnname = "pg_catalog.json_object_agg_unique"; /* F_JSON_OBJECT_AGG_UNIQUE */
|
||||
else
|
||||
aggfnname = "pg_catalog.json_object_agg"; /* F_JSON_OBJECT_AGG */
|
||||
|
||||
aggtype = JSONOID;
|
||||
}
|
||||
|
||||
return transformJsonAggConstructor(pstate, agg->constructor, returning,
|
||||
args, aggfnname, aggtype,
|
||||
JSCTOR_JSON_OBJECTAGG,
|
||||
agg->unique, agg->absent_on_null);
|
||||
}
|
||||
|
||||
/*
|
||||
* Transform JSON_ARRAYAGG() aggregate function.
|
||||
*
|
||||
* JSON_ARRAYAGG() is transformed into json[b]_agg[_strict]() call depending
|
||||
* on the output JSON format and absent_on_null. Then the function call result
|
||||
* is coerced to the target output type.
|
||||
*/
|
||||
static Node *
|
||||
transformJsonArrayAgg(ParseState *pstate, JsonArrayAgg *agg)
|
||||
{
|
||||
JsonReturning *returning;
|
||||
Node *arg;
|
||||
const char *aggfnname;
|
||||
Oid aggtype;
|
||||
|
||||
arg = transformJsonValueExpr(pstate, agg->arg, JS_FORMAT_DEFAULT);
|
||||
|
||||
returning = transformJsonConstructorOutput(pstate, agg->constructor->output,
|
||||
list_make1(arg));
|
||||
|
||||
if (returning->format->format_type == JS_FORMAT_JSONB)
|
||||
{
|
||||
aggfnname = agg->absent_on_null ?
|
||||
"pg_catalog.jsonb_agg_strict" : "pg_catalog.jsonb_agg";
|
||||
aggtype = JSONBOID;
|
||||
}
|
||||
else
|
||||
{
|
||||
aggfnname = agg->absent_on_null ?
|
||||
"pg_catalog.json_agg_strict" : "pg_catalog.json_agg";
|
||||
aggtype = JSONOID;
|
||||
}
|
||||
|
||||
return transformJsonAggConstructor(pstate, agg->constructor, returning,
|
||||
list_make1(arg), aggfnname, aggtype,
|
||||
JSCTOR_JSON_ARRAYAGG,
|
||||
false, agg->absent_on_null);
|
||||
}
|
||||
|
||||
/*
|
||||
* Transform JSON_ARRAY() constructor.
|
||||
*
|
||||
* JSON_ARRAY() is transformed into json[b]_build_array[_ext]() call
|
||||
* depending on the output JSON format. The first argument of
|
||||
* json[b]_build_array_ext() is absent_on_null.
|
||||
*
|
||||
* Then function call result is coerced to the target type.
|
||||
*/
|
||||
static Node *
|
||||
transformJsonArrayConstructor(ParseState *pstate, JsonArrayConstructor *ctor)
|
||||
{
|
||||
JsonReturning *returning;
|
||||
List *args = NIL;
|
||||
|
||||
/* transform element expressions, if any */
|
||||
if (ctor->exprs)
|
||||
{
|
||||
ListCell *lc;
|
||||
|
||||
/* transform and append element arguments */
|
||||
foreach(lc, ctor->exprs)
|
||||
{
|
||||
JsonValueExpr *jsval = castNode(JsonValueExpr, lfirst(lc));
|
||||
Node *val = transformJsonValueExpr(pstate, jsval,
|
||||
JS_FORMAT_DEFAULT);
|
||||
|
||||
args = lappend(args, val);
|
||||
}
|
||||
}
|
||||
|
||||
returning = transformJsonConstructorOutput(pstate, ctor->output, args);
|
||||
|
||||
return makeJsonConstructorExpr(pstate, JSCTOR_JSON_ARRAY, args, NULL,
|
||||
returning, false, ctor->absent_on_null,
|
||||
ctor->location);
|
||||
}
|
||||
|
@ -1957,6 +1957,19 @@ FigureColnameInternal(Node *node, char **name)
|
||||
case T_XmlSerialize:
|
||||
*name = "xmlserialize";
|
||||
return 2;
|
||||
case T_JsonObjectConstructor:
|
||||
*name = "json_object";
|
||||
return 2;
|
||||
case T_JsonArrayConstructor:
|
||||
case T_JsonArrayQueryConstructor:
|
||||
*name = "json_array";
|
||||
return 2;
|
||||
case T_JsonObjectAgg:
|
||||
*name = "json_objectagg";
|
||||
return 2;
|
||||
case T_JsonArrayAgg:
|
||||
*name = "json_arrayagg";
|
||||
return 2;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -150,6 +150,9 @@ base_yylex(YYSTYPE *lvalp, YYLTYPE *llocp, core_yyscan_t yyscanner)
|
||||
case USCONST:
|
||||
cur_token_length = strlen(yyextra->core_yy_extra.scanbuf + *llocp);
|
||||
break;
|
||||
case WITHOUT:
|
||||
cur_token_length = 7;
|
||||
break;
|
||||
default:
|
||||
return cur_token;
|
||||
}
|
||||
@ -221,6 +224,19 @@ base_yylex(YYSTYPE *lvalp, YYLTYPE *llocp, core_yyscan_t yyscanner)
|
||||
case ORDINALITY:
|
||||
cur_token = WITH_LA;
|
||||
break;
|
||||
case UNIQUE:
|
||||
cur_token = WITH_LA_UNIQUE;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case WITHOUT:
|
||||
/* Replace WITHOUT by WITHOUT_LA if it's followed by TIME */
|
||||
switch (next_token)
|
||||
{
|
||||
case TIME:
|
||||
cur_token = WITHOUT_LA;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -13,7 +13,9 @@
|
||||
*/
|
||||
#include "postgres.h"
|
||||
|
||||
#include "catalog/pg_proc.h"
|
||||
#include "catalog/pg_type.h"
|
||||
#include "common/hashfn.h"
|
||||
#include "funcapi.h"
|
||||
#include "libpq/pqformat.h"
|
||||
#include "miscadmin.h"
|
||||
@ -42,6 +44,42 @@ typedef enum /* type categories for datum_to_json */
|
||||
JSONTYPE_OTHER /* all else */
|
||||
} JsonTypeCategory;
|
||||
|
||||
/* Common context for key uniqueness check */
|
||||
typedef struct HTAB *JsonUniqueCheckState; /* hash table for key names */
|
||||
|
||||
/* Hash entry for JsonUniqueCheckState */
|
||||
typedef struct JsonUniqueHashEntry
|
||||
{
|
||||
const char *key;
|
||||
int key_len;
|
||||
int object_id;
|
||||
} JsonUniqueHashEntry;
|
||||
|
||||
/* Context for key uniqueness check in builder functions */
|
||||
typedef struct JsonUniqueBuilderState
|
||||
{
|
||||
JsonUniqueCheckState check; /* unique check */
|
||||
StringInfoData skipped_keys; /* skipped keys with NULL values */
|
||||
MemoryContext mcxt; /* context for saving skipped keys */
|
||||
} JsonUniqueBuilderState;
|
||||
|
||||
/* Element of object stack for key uniqueness check during json parsing */
|
||||
typedef struct JsonUniqueStackEntry
|
||||
{
|
||||
struct JsonUniqueStackEntry *parent;
|
||||
int object_id;
|
||||
} JsonUniqueStackEntry;
|
||||
|
||||
/* State for key uniqueness check during json parsing */
|
||||
typedef struct JsonUniqueParsingState
|
||||
{
|
||||
JsonLexContext *lex;
|
||||
JsonUniqueCheckState check;
|
||||
JsonUniqueStackEntry *stack;
|
||||
int id_counter;
|
||||
bool unique;
|
||||
} JsonUniqueParsingState;
|
||||
|
||||
typedef struct JsonAggState
|
||||
{
|
||||
StringInfo str;
|
||||
@ -49,6 +87,7 @@ typedef struct JsonAggState
|
||||
Oid key_output_func;
|
||||
JsonTypeCategory val_category;
|
||||
Oid val_output_func;
|
||||
JsonUniqueBuilderState unique_check;
|
||||
} JsonAggState;
|
||||
|
||||
static void composite_to_json(Datum composite, StringInfo result,
|
||||
@ -722,6 +761,38 @@ row_to_json_pretty(PG_FUNCTION_ARGS)
|
||||
PG_RETURN_TEXT_P(cstring_to_text_with_len(result->data, result->len));
|
||||
}
|
||||
|
||||
bool
|
||||
to_json_is_immutable(Oid typoid)
|
||||
{
|
||||
JsonTypeCategory tcategory;
|
||||
Oid outfuncoid;
|
||||
|
||||
json_categorize_type(typoid, &tcategory, &outfuncoid);
|
||||
|
||||
switch (tcategory)
|
||||
{
|
||||
case JSONTYPE_BOOL:
|
||||
case JSONTYPE_JSON:
|
||||
return true;
|
||||
|
||||
case JSONTYPE_DATE:
|
||||
case JSONTYPE_TIMESTAMP:
|
||||
case JSONTYPE_TIMESTAMPTZ:
|
||||
return false;
|
||||
|
||||
case JSONTYPE_ARRAY:
|
||||
return false; /* TODO recurse into elements */
|
||||
|
||||
case JSONTYPE_COMPOSITE:
|
||||
return false; /* TODO recurse into fields */
|
||||
|
||||
case JSONTYPE_NUMERIC:
|
||||
case JSONTYPE_CAST:
|
||||
default:
|
||||
return func_volatile(outfuncoid) == PROVOLATILE_IMMUTABLE;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* SQL function to_json(anyvalue)
|
||||
*/
|
||||
@ -754,8 +825,8 @@ to_json(PG_FUNCTION_ARGS)
|
||||
*
|
||||
* aggregate input column as a json array value.
|
||||
*/
|
||||
Datum
|
||||
json_agg_transfn(PG_FUNCTION_ARGS)
|
||||
static Datum
|
||||
json_agg_transfn_worker(FunctionCallInfo fcinfo, bool absent_on_null)
|
||||
{
|
||||
MemoryContext aggcontext,
|
||||
oldcontext;
|
||||
@ -795,9 +866,14 @@ json_agg_transfn(PG_FUNCTION_ARGS)
|
||||
else
|
||||
{
|
||||
state = (JsonAggState *) PG_GETARG_POINTER(0);
|
||||
appendStringInfoString(state->str, ", ");
|
||||
}
|
||||
|
||||
if (absent_on_null && PG_ARGISNULL(1))
|
||||
PG_RETURN_POINTER(state);
|
||||
|
||||
if (state->str->len > 1)
|
||||
appendStringInfoString(state->str, ", ");
|
||||
|
||||
/* fast path for NULLs */
|
||||
if (PG_ARGISNULL(1))
|
||||
{
|
||||
@ -809,7 +885,7 @@ json_agg_transfn(PG_FUNCTION_ARGS)
|
||||
val = PG_GETARG_DATUM(1);
|
||||
|
||||
/* add some whitespace if structured type and not first item */
|
||||
if (!PG_ARGISNULL(0) &&
|
||||
if (!PG_ARGISNULL(0) && state->str->len > 1 &&
|
||||
(state->val_category == JSONTYPE_ARRAY ||
|
||||
state->val_category == JSONTYPE_COMPOSITE))
|
||||
{
|
||||
@ -827,6 +903,25 @@ json_agg_transfn(PG_FUNCTION_ARGS)
|
||||
PG_RETURN_POINTER(state);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* json_agg aggregate function
|
||||
*/
|
||||
Datum
|
||||
json_agg_transfn(PG_FUNCTION_ARGS)
|
||||
{
|
||||
return json_agg_transfn_worker(fcinfo, false);
|
||||
}
|
||||
|
||||
/*
|
||||
* json_agg_strict aggregate function
|
||||
*/
|
||||
Datum
|
||||
json_agg_strict_transfn(PG_FUNCTION_ARGS)
|
||||
{
|
||||
return json_agg_transfn_worker(fcinfo, true);
|
||||
}
|
||||
|
||||
/*
|
||||
* json_agg final function
|
||||
*/
|
||||
@ -850,18 +945,122 @@ json_agg_finalfn(PG_FUNCTION_ARGS)
|
||||
PG_RETURN_TEXT_P(catenate_stringinfo_string(state->str, "]"));
|
||||
}
|
||||
|
||||
/* Functions implementing hash table for key uniqueness check */
|
||||
static uint32
|
||||
json_unique_hash(const void *key, Size keysize)
|
||||
{
|
||||
const JsonUniqueHashEntry *entry = (JsonUniqueHashEntry *) key;
|
||||
uint32 hash = hash_bytes_uint32(entry->object_id);
|
||||
|
||||
hash ^= hash_bytes((const unsigned char *) entry->key, entry->key_len);
|
||||
|
||||
return DatumGetUInt32(hash);
|
||||
}
|
||||
|
||||
static int
|
||||
json_unique_hash_match(const void *key1, const void *key2, Size keysize)
|
||||
{
|
||||
const JsonUniqueHashEntry *entry1 = (const JsonUniqueHashEntry *) key1;
|
||||
const JsonUniqueHashEntry *entry2 = (const JsonUniqueHashEntry *) key2;
|
||||
|
||||
if (entry1->object_id != entry2->object_id)
|
||||
return entry1->object_id > entry2->object_id ? 1 : -1;
|
||||
|
||||
if (entry1->key_len != entry2->key_len)
|
||||
return entry1->key_len > entry2->key_len ? 1 : -1;
|
||||
|
||||
return strncmp(entry1->key, entry2->key, entry1->key_len);
|
||||
}
|
||||
|
||||
/* Functions implementing object key uniqueness check */
|
||||
static void
|
||||
json_unique_check_init(JsonUniqueCheckState *cxt)
|
||||
{
|
||||
HASHCTL ctl;
|
||||
|
||||
memset(&ctl, 0, sizeof(ctl));
|
||||
ctl.keysize = sizeof(JsonUniqueHashEntry);
|
||||
ctl.entrysize = sizeof(JsonUniqueHashEntry);
|
||||
ctl.hcxt = CurrentMemoryContext;
|
||||
ctl.hash = json_unique_hash;
|
||||
ctl.match = json_unique_hash_match;
|
||||
|
||||
*cxt = hash_create("json object hashtable",
|
||||
32,
|
||||
&ctl,
|
||||
HASH_ELEM | HASH_CONTEXT | HASH_FUNCTION | HASH_COMPARE);
|
||||
}
|
||||
|
||||
static void
|
||||
json_unique_check_free(JsonUniqueCheckState *cxt)
|
||||
{
|
||||
hash_destroy(*cxt);
|
||||
}
|
||||
|
||||
static bool
|
||||
json_unique_check_key(JsonUniqueCheckState *cxt, const char *key, int object_id)
|
||||
{
|
||||
JsonUniqueHashEntry entry;
|
||||
bool found;
|
||||
|
||||
entry.key = key;
|
||||
entry.key_len = strlen(key);
|
||||
entry.object_id = object_id;
|
||||
|
||||
(void) hash_search(*cxt, &entry, HASH_ENTER, &found);
|
||||
|
||||
return !found;
|
||||
}
|
||||
|
||||
static void
|
||||
json_unique_builder_init(JsonUniqueBuilderState *cxt)
|
||||
{
|
||||
json_unique_check_init(&cxt->check);
|
||||
cxt->mcxt = CurrentMemoryContext;
|
||||
cxt->skipped_keys.data = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
json_unique_builder_free(JsonUniqueBuilderState *cxt)
|
||||
{
|
||||
json_unique_check_free(&cxt->check);
|
||||
|
||||
if (cxt->skipped_keys.data)
|
||||
pfree(cxt->skipped_keys.data);
|
||||
}
|
||||
|
||||
/* On-demand initialization of skipped_keys StringInfo structure */
|
||||
static StringInfo
|
||||
json_unique_builder_get_skipped_keys(JsonUniqueBuilderState *cxt)
|
||||
{
|
||||
StringInfo out = &cxt->skipped_keys;
|
||||
|
||||
if (!out->data)
|
||||
{
|
||||
MemoryContext oldcxt = MemoryContextSwitchTo(cxt->mcxt);
|
||||
initStringInfo(out);
|
||||
MemoryContextSwitchTo(oldcxt);
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
/*
|
||||
* json_object_agg transition function.
|
||||
*
|
||||
* aggregate two input columns as a single json object value.
|
||||
*/
|
||||
Datum
|
||||
json_object_agg_transfn(PG_FUNCTION_ARGS)
|
||||
static Datum
|
||||
json_object_agg_transfn_worker(FunctionCallInfo fcinfo,
|
||||
bool absent_on_null, bool unique_keys)
|
||||
{
|
||||
MemoryContext aggcontext,
|
||||
oldcontext;
|
||||
JsonAggState *state;
|
||||
StringInfo out;
|
||||
Datum arg;
|
||||
bool skip;
|
||||
int key_offset;
|
||||
|
||||
if (!AggCheckCallContext(fcinfo, &aggcontext))
|
||||
{
|
||||
@ -882,6 +1081,10 @@ json_object_agg_transfn(PG_FUNCTION_ARGS)
|
||||
oldcontext = MemoryContextSwitchTo(aggcontext);
|
||||
state = (JsonAggState *) palloc(sizeof(JsonAggState));
|
||||
state->str = makeStringInfo();
|
||||
if (unique_keys)
|
||||
json_unique_builder_init(&state->unique_check);
|
||||
else
|
||||
memset(&state->unique_check, 0, sizeof(state->unique_check));
|
||||
MemoryContextSwitchTo(oldcontext);
|
||||
|
||||
arg_type = get_fn_expr_argtype(fcinfo->flinfo, 1);
|
||||
@ -909,7 +1112,6 @@ json_object_agg_transfn(PG_FUNCTION_ARGS)
|
||||
else
|
||||
{
|
||||
state = (JsonAggState *) PG_GETARG_POINTER(0);
|
||||
appendStringInfoString(state->str, ", ");
|
||||
}
|
||||
|
||||
/*
|
||||
@ -925,11 +1127,49 @@ json_object_agg_transfn(PG_FUNCTION_ARGS)
|
||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||
errmsg("field name must not be null")));
|
||||
|
||||
/* Skip null values if absent_on_null */
|
||||
skip = absent_on_null && PG_ARGISNULL(2);
|
||||
|
||||
if (skip)
|
||||
{
|
||||
/* If key uniqueness check is needed we must save skipped keys */
|
||||
if (!unique_keys)
|
||||
PG_RETURN_POINTER(state);
|
||||
|
||||
out = json_unique_builder_get_skipped_keys(&state->unique_check);
|
||||
}
|
||||
else
|
||||
{
|
||||
out = state->str;
|
||||
|
||||
/*
|
||||
* Append comma delimiter only if we have already outputted some fields
|
||||
* after the initial string "{ ".
|
||||
*/
|
||||
if (out->len > 2)
|
||||
appendStringInfoString(out, ", ");
|
||||
}
|
||||
|
||||
arg = PG_GETARG_DATUM(1);
|
||||
|
||||
datum_to_json(arg, false, state->str, state->key_category,
|
||||
key_offset = out->len;
|
||||
|
||||
datum_to_json(arg, false, out, state->key_category,
|
||||
state->key_output_func, true);
|
||||
|
||||
if (unique_keys)
|
||||
{
|
||||
const char *key = &out->data[key_offset];
|
||||
|
||||
if (!json_unique_check_key(&state->unique_check.check, key, 0))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_DUPLICATE_JSON_OBJECT_KEY_VALUE),
|
||||
errmsg("duplicate JSON key %s", key)));
|
||||
|
||||
if (skip)
|
||||
PG_RETURN_POINTER(state);
|
||||
}
|
||||
|
||||
appendStringInfoString(state->str, " : ");
|
||||
|
||||
if (PG_ARGISNULL(2))
|
||||
@ -943,6 +1183,42 @@ json_object_agg_transfn(PG_FUNCTION_ARGS)
|
||||
PG_RETURN_POINTER(state);
|
||||
}
|
||||
|
||||
/*
|
||||
* json_object_agg aggregate function
|
||||
*/
|
||||
Datum
|
||||
json_object_agg_transfn(PG_FUNCTION_ARGS)
|
||||
{
|
||||
return json_object_agg_transfn_worker(fcinfo, false, false);
|
||||
}
|
||||
|
||||
/*
|
||||
* json_object_agg_strict aggregate function
|
||||
*/
|
||||
Datum
|
||||
json_object_agg_strict_transfn(PG_FUNCTION_ARGS)
|
||||
{
|
||||
return json_object_agg_transfn_worker(fcinfo, true, false);
|
||||
}
|
||||
|
||||
/*
|
||||
* json_object_agg_unique aggregate function
|
||||
*/
|
||||
Datum
|
||||
json_object_agg_unique_transfn(PG_FUNCTION_ARGS)
|
||||
{
|
||||
return json_object_agg_transfn_worker(fcinfo, false, true);
|
||||
}
|
||||
|
||||
/*
|
||||
* json_object_agg_unique_strict aggregate function
|
||||
*/
|
||||
Datum
|
||||
json_object_agg_unique_strict_transfn(PG_FUNCTION_ARGS)
|
||||
{
|
||||
return json_object_agg_transfn_worker(fcinfo, true, true);
|
||||
}
|
||||
|
||||
/*
|
||||
* json_object_agg final function.
|
||||
*/
|
||||
@ -960,6 +1236,8 @@ json_object_agg_finalfn(PG_FUNCTION_ARGS)
|
||||
if (state == NULL)
|
||||
PG_RETURN_NULL();
|
||||
|
||||
json_unique_builder_free(&state->unique_check);
|
||||
|
||||
/* Else return state with appropriate object terminator added */
|
||||
PG_RETURN_TEXT_P(catenate_stringinfo_string(state->str, " }"));
|
||||
}
|
||||
@ -984,25 +1262,14 @@ catenate_stringinfo_string(StringInfo buffer, const char *addon)
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* SQL function json_build_object(variadic "any")
|
||||
*/
|
||||
Datum
|
||||
json_build_object(PG_FUNCTION_ARGS)
|
||||
json_build_object_worker(int nargs, Datum *args, bool *nulls, Oid *types,
|
||||
bool absent_on_null, bool unique_keys)
|
||||
{
|
||||
int nargs;
|
||||
int i;
|
||||
const char *sep = "";
|
||||
StringInfo result;
|
||||
Datum *args;
|
||||
bool *nulls;
|
||||
Oid *types;
|
||||
|
||||
/* fetch argument values to build the object */
|
||||
nargs = extract_variadic_args(fcinfo, 0, false, &args, &types, &nulls);
|
||||
|
||||
if (nargs < 0)
|
||||
PG_RETURN_NULL();
|
||||
JsonUniqueBuilderState unique_check;
|
||||
|
||||
if (nargs % 2 != 0)
|
||||
ereport(ERROR,
|
||||
@ -1016,10 +1283,32 @@ json_build_object(PG_FUNCTION_ARGS)
|
||||
|
||||
appendStringInfoChar(result, '{');
|
||||
|
||||
if (unique_keys)
|
||||
json_unique_builder_init(&unique_check);
|
||||
|
||||
for (i = 0; i < nargs; i += 2)
|
||||
{
|
||||
StringInfo out;
|
||||
bool skip;
|
||||
int key_offset;
|
||||
|
||||
/* Skip null values if absent_on_null */
|
||||
skip = absent_on_null && nulls[i + 1];
|
||||
|
||||
if (skip)
|
||||
{
|
||||
/* If key uniqueness check is needed we must save skipped keys */
|
||||
if (!unique_keys)
|
||||
continue;
|
||||
|
||||
out = json_unique_builder_get_skipped_keys(&unique_check);
|
||||
}
|
||||
else
|
||||
{
|
||||
appendStringInfoString(result, sep);
|
||||
sep = ", ";
|
||||
out = result;
|
||||
}
|
||||
|
||||
/* process key */
|
||||
if (nulls[i])
|
||||
@ -1028,7 +1317,24 @@ json_build_object(PG_FUNCTION_ARGS)
|
||||
errmsg("argument %d cannot be null", i + 1),
|
||||
errhint("Object keys should be text.")));
|
||||
|
||||
add_json(args[i], false, result, types[i], true);
|
||||
/* save key offset before key appending */
|
||||
key_offset = out->len;
|
||||
|
||||
add_json(args[i], false, out, types[i], true);
|
||||
|
||||
if (unique_keys)
|
||||
{
|
||||
/* check key uniqueness after key appending */
|
||||
const char *key = &out->data[key_offset];
|
||||
|
||||
if (!json_unique_check_key(&unique_check.check, key, 0))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_DUPLICATE_JSON_OBJECT_KEY_VALUE),
|
||||
errmsg("duplicate JSON key %s", key)));
|
||||
|
||||
if (skip)
|
||||
continue;
|
||||
}
|
||||
|
||||
appendStringInfoString(result, " : ");
|
||||
|
||||
@ -1038,7 +1344,29 @@ json_build_object(PG_FUNCTION_ARGS)
|
||||
|
||||
appendStringInfoChar(result, '}');
|
||||
|
||||
PG_RETURN_TEXT_P(cstring_to_text_with_len(result->data, result->len));
|
||||
if (unique_keys)
|
||||
json_unique_builder_free(&unique_check);
|
||||
|
||||
return PointerGetDatum(cstring_to_text_with_len(result->data, result->len));
|
||||
}
|
||||
|
||||
/*
|
||||
* SQL function json_build_object(variadic "any")
|
||||
*/
|
||||
Datum
|
||||
json_build_object(PG_FUNCTION_ARGS)
|
||||
{
|
||||
Datum *args;
|
||||
bool *nulls;
|
||||
Oid *types;
|
||||
/* build argument values to build the object */
|
||||
int nargs = extract_variadic_args(fcinfo, 0, true,
|
||||
&args, &types, &nulls);
|
||||
|
||||
if (nargs < 0)
|
||||
PG_RETURN_NULL();
|
||||
|
||||
PG_RETURN_DATUM(json_build_object_worker(nargs, args, nulls, types, false, false));
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1050,25 +1378,13 @@ json_build_object_noargs(PG_FUNCTION_ARGS)
|
||||
PG_RETURN_TEXT_P(cstring_to_text_with_len("{}", 2));
|
||||
}
|
||||
|
||||
/*
|
||||
* SQL function json_build_array(variadic "any")
|
||||
*/
|
||||
Datum
|
||||
json_build_array(PG_FUNCTION_ARGS)
|
||||
json_build_array_worker(int nargs, Datum *args, bool *nulls, Oid *types,
|
||||
bool absent_on_null)
|
||||
{
|
||||
int nargs;
|
||||
int i;
|
||||
const char *sep = "";
|
||||
StringInfo result;
|
||||
Datum *args;
|
||||
bool *nulls;
|
||||
Oid *types;
|
||||
|
||||
/* fetch argument values to build the array */
|
||||
nargs = extract_variadic_args(fcinfo, 0, false, &args, &types, &nulls);
|
||||
|
||||
if (nargs < 0)
|
||||
PG_RETURN_NULL();
|
||||
|
||||
result = makeStringInfo();
|
||||
|
||||
@ -1076,6 +1392,9 @@ json_build_array(PG_FUNCTION_ARGS)
|
||||
|
||||
for (i = 0; i < nargs; i++)
|
||||
{
|
||||
if (absent_on_null && nulls[i])
|
||||
continue;
|
||||
|
||||
appendStringInfoString(result, sep);
|
||||
sep = ", ";
|
||||
add_json(args[i], nulls[i], result, types[i], false);
|
||||
@ -1083,7 +1402,26 @@ json_build_array(PG_FUNCTION_ARGS)
|
||||
|
||||
appendStringInfoChar(result, ']');
|
||||
|
||||
PG_RETURN_TEXT_P(cstring_to_text_with_len(result->data, result->len));
|
||||
return PointerGetDatum(cstring_to_text_with_len(result->data, result->len));
|
||||
}
|
||||
|
||||
/*
|
||||
* SQL function json_build_array(variadic "any")
|
||||
*/
|
||||
Datum
|
||||
json_build_array(PG_FUNCTION_ARGS)
|
||||
{
|
||||
Datum *args;
|
||||
bool *nulls;
|
||||
Oid *types;
|
||||
/* build argument values to build the object */
|
||||
int nargs = extract_variadic_args(fcinfo, 0, true,
|
||||
&args, &types, &nulls);
|
||||
|
||||
if (nargs < 0)
|
||||
PG_RETURN_NULL();
|
||||
|
||||
PG_RETURN_DATUM(json_build_array_worker(nargs, args, nulls, types, false));
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -14,6 +14,7 @@
|
||||
|
||||
#include "access/htup_details.h"
|
||||
#include "access/transam.h"
|
||||
#include "catalog/pg_proc.h"
|
||||
#include "catalog/pg_type.h"
|
||||
#include "funcapi.h"
|
||||
#include "libpq/pqformat.h"
|
||||
@ -1126,6 +1127,39 @@ add_jsonb(Datum val, bool is_null, JsonbInState *result,
|
||||
datum_to_jsonb(val, is_null, result, tcategory, outfuncoid, key_scalar);
|
||||
}
|
||||
|
||||
bool
|
||||
to_jsonb_is_immutable(Oid typoid)
|
||||
{
|
||||
JsonbTypeCategory tcategory;
|
||||
Oid outfuncoid;
|
||||
|
||||
jsonb_categorize_type(typoid, &tcategory, &outfuncoid);
|
||||
|
||||
switch (tcategory)
|
||||
{
|
||||
case JSONBTYPE_BOOL:
|
||||
case JSONBTYPE_JSON:
|
||||
case JSONBTYPE_JSONB:
|
||||
return true;
|
||||
|
||||
case JSONBTYPE_DATE:
|
||||
case JSONBTYPE_TIMESTAMP:
|
||||
case JSONBTYPE_TIMESTAMPTZ:
|
||||
return false;
|
||||
|
||||
case JSONBTYPE_ARRAY:
|
||||
return false; /* TODO recurse into elements */
|
||||
|
||||
case JSONBTYPE_COMPOSITE:
|
||||
return false; /* TODO recurse into fields */
|
||||
|
||||
case JSONBTYPE_NUMERIC:
|
||||
case JSONBTYPE_JSONCAST:
|
||||
default:
|
||||
return func_volatile(outfuncoid) == PROVOLATILE_IMMUTABLE;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* SQL function to_jsonb(anyvalue)
|
||||
*/
|
||||
@ -1153,24 +1187,12 @@ to_jsonb(PG_FUNCTION_ARGS)
|
||||
PG_RETURN_POINTER(JsonbValueToJsonb(result.res));
|
||||
}
|
||||
|
||||
/*
|
||||
* SQL function jsonb_build_object(variadic "any")
|
||||
*/
|
||||
Datum
|
||||
jsonb_build_object(PG_FUNCTION_ARGS)
|
||||
jsonb_build_object_worker(int nargs, Datum *args, bool *nulls, Oid *types,
|
||||
bool absent_on_null, bool unique_keys)
|
||||
{
|
||||
int nargs;
|
||||
int i;
|
||||
JsonbInState result;
|
||||
Datum *args;
|
||||
bool *nulls;
|
||||
Oid *types;
|
||||
|
||||
/* build argument values to build the object */
|
||||
nargs = extract_variadic_args(fcinfo, 0, true, &args, &types, &nulls);
|
||||
|
||||
if (nargs < 0)
|
||||
PG_RETURN_NULL();
|
||||
|
||||
if (nargs % 2 != 0)
|
||||
ereport(ERROR,
|
||||
@ -1183,15 +1205,26 @@ jsonb_build_object(PG_FUNCTION_ARGS)
|
||||
memset(&result, 0, sizeof(JsonbInState));
|
||||
|
||||
result.res = pushJsonbValue(&result.parseState, WJB_BEGIN_OBJECT, NULL);
|
||||
result.parseState->unique_keys = unique_keys;
|
||||
result.parseState->skip_nulls = absent_on_null;
|
||||
|
||||
for (i = 0; i < nargs; i += 2)
|
||||
{
|
||||
/* process key */
|
||||
bool skip;
|
||||
|
||||
if (nulls[i])
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||
errmsg("argument %d: key must not be null", i + 1)));
|
||||
|
||||
/* skip null values if absent_on_null */
|
||||
skip = absent_on_null && nulls[i + 1];
|
||||
|
||||
/* we need to save skipped keys for the key uniqueness check */
|
||||
if (skip && !unique_keys)
|
||||
continue;
|
||||
|
||||
add_jsonb(args[i], false, &result, types[i], true);
|
||||
|
||||
/* process value */
|
||||
@ -1200,7 +1233,26 @@ jsonb_build_object(PG_FUNCTION_ARGS)
|
||||
|
||||
result.res = pushJsonbValue(&result.parseState, WJB_END_OBJECT, NULL);
|
||||
|
||||
PG_RETURN_POINTER(JsonbValueToJsonb(result.res));
|
||||
return JsonbPGetDatum(JsonbValueToJsonb(result.res));
|
||||
}
|
||||
|
||||
/*
|
||||
* SQL function jsonb_build_object(variadic "any")
|
||||
*/
|
||||
Datum
|
||||
jsonb_build_object(PG_FUNCTION_ARGS)
|
||||
{
|
||||
Datum *args;
|
||||
bool *nulls;
|
||||
Oid *types;
|
||||
/* build argument values to build the object */
|
||||
int nargs = extract_variadic_args(fcinfo, 0, true,
|
||||
&args, &types, &nulls);
|
||||
|
||||
if (nargs < 0)
|
||||
PG_RETURN_NULL();
|
||||
|
||||
PG_RETURN_DATUM(jsonb_build_object_worker(nargs, args, nulls, types, false, false));
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1219,37 +1271,50 @@ jsonb_build_object_noargs(PG_FUNCTION_ARGS)
|
||||
PG_RETURN_POINTER(JsonbValueToJsonb(result.res));
|
||||
}
|
||||
|
||||
/*
|
||||
* SQL function jsonb_build_array(variadic "any")
|
||||
*/
|
||||
Datum
|
||||
jsonb_build_array(PG_FUNCTION_ARGS)
|
||||
jsonb_build_array_worker(int nargs, Datum *args, bool *nulls, Oid *types,
|
||||
bool absent_on_null)
|
||||
{
|
||||
int nargs;
|
||||
int i;
|
||||
JsonbInState result;
|
||||
Datum *args;
|
||||
bool *nulls;
|
||||
Oid *types;
|
||||
|
||||
/* build argument values to build the array */
|
||||
nargs = extract_variadic_args(fcinfo, 0, true, &args, &types, &nulls);
|
||||
|
||||
if (nargs < 0)
|
||||
PG_RETURN_NULL();
|
||||
|
||||
memset(&result, 0, sizeof(JsonbInState));
|
||||
|
||||
result.res = pushJsonbValue(&result.parseState, WJB_BEGIN_ARRAY, NULL);
|
||||
|
||||
for (i = 0; i < nargs; i++)
|
||||
{
|
||||
if (absent_on_null && nulls[i])
|
||||
continue;
|
||||
|
||||
add_jsonb(args[i], nulls[i], &result, types[i], false);
|
||||
}
|
||||
|
||||
result.res = pushJsonbValue(&result.parseState, WJB_END_ARRAY, NULL);
|
||||
|
||||
PG_RETURN_POINTER(JsonbValueToJsonb(result.res));
|
||||
return JsonbPGetDatum(JsonbValueToJsonb(result.res));
|
||||
}
|
||||
|
||||
/*
|
||||
* SQL function jsonb_build_array(variadic "any")
|
||||
*/
|
||||
Datum
|
||||
jsonb_build_array(PG_FUNCTION_ARGS)
|
||||
{
|
||||
Datum *args;
|
||||
bool *nulls;
|
||||
Oid *types;
|
||||
/* build argument values to build the object */
|
||||
int nargs = extract_variadic_args(fcinfo, 0, true,
|
||||
&args, &types, &nulls);
|
||||
|
||||
if (nargs < 0)
|
||||
PG_RETURN_NULL();
|
||||
|
||||
PG_RETURN_DATUM(jsonb_build_array_worker(nargs, args, nulls, types, false));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* degenerate case of jsonb_build_array where it gets 0 arguments.
|
||||
*/
|
||||
@ -1490,6 +1555,8 @@ clone_parse_state(JsonbParseState *state)
|
||||
{
|
||||
ocursor->contVal = icursor->contVal;
|
||||
ocursor->size = icursor->size;
|
||||
ocursor->unique_keys = icursor->unique_keys;
|
||||
ocursor->skip_nulls = icursor->skip_nulls;
|
||||
icursor = icursor->next;
|
||||
if (icursor == NULL)
|
||||
break;
|
||||
@ -1501,12 +1568,8 @@ clone_parse_state(JsonbParseState *state)
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* jsonb_agg aggregate function
|
||||
*/
|
||||
Datum
|
||||
jsonb_agg_transfn(PG_FUNCTION_ARGS)
|
||||
static Datum
|
||||
jsonb_agg_transfn_worker(FunctionCallInfo fcinfo, bool absent_on_null)
|
||||
{
|
||||
MemoryContext oldcontext,
|
||||
aggcontext;
|
||||
@ -1554,6 +1617,9 @@ jsonb_agg_transfn(PG_FUNCTION_ARGS)
|
||||
result = state->res;
|
||||
}
|
||||
|
||||
if (absent_on_null && PG_ARGISNULL(1))
|
||||
PG_RETURN_POINTER(state);
|
||||
|
||||
/* turn the argument into jsonb in the normal function context */
|
||||
|
||||
val = PG_ARGISNULL(1) ? (Datum) 0 : PG_GETARG_DATUM(1);
|
||||
@ -1623,6 +1689,24 @@ jsonb_agg_transfn(PG_FUNCTION_ARGS)
|
||||
PG_RETURN_POINTER(state);
|
||||
}
|
||||
|
||||
/*
|
||||
* jsonb_agg aggregate function
|
||||
*/
|
||||
Datum
|
||||
jsonb_agg_transfn(PG_FUNCTION_ARGS)
|
||||
{
|
||||
return jsonb_agg_transfn_worker(fcinfo, false);
|
||||
}
|
||||
|
||||
/*
|
||||
* jsonb_agg_strict aggregate function
|
||||
*/
|
||||
Datum
|
||||
jsonb_agg_strict_transfn(PG_FUNCTION_ARGS)
|
||||
{
|
||||
return jsonb_agg_transfn_worker(fcinfo, true);
|
||||
}
|
||||
|
||||
Datum
|
||||
jsonb_agg_finalfn(PG_FUNCTION_ARGS)
|
||||
{
|
||||
@ -1655,11 +1739,9 @@ jsonb_agg_finalfn(PG_FUNCTION_ARGS)
|
||||
PG_RETURN_POINTER(out);
|
||||
}
|
||||
|
||||
/*
|
||||
* jsonb_object_agg aggregate function
|
||||
*/
|
||||
Datum
|
||||
jsonb_object_agg_transfn(PG_FUNCTION_ARGS)
|
||||
static Datum
|
||||
jsonb_object_agg_transfn_worker(FunctionCallInfo fcinfo,
|
||||
bool absent_on_null, bool unique_keys)
|
||||
{
|
||||
MemoryContext oldcontext,
|
||||
aggcontext;
|
||||
@ -1673,6 +1755,7 @@ jsonb_object_agg_transfn(PG_FUNCTION_ARGS)
|
||||
*jbval;
|
||||
JsonbValue v;
|
||||
JsonbIteratorToken type;
|
||||
bool skip;
|
||||
|
||||
if (!AggCheckCallContext(fcinfo, &aggcontext))
|
||||
{
|
||||
@ -1692,6 +1775,9 @@ jsonb_object_agg_transfn(PG_FUNCTION_ARGS)
|
||||
state->res = result;
|
||||
result->res = pushJsonbValue(&result->parseState,
|
||||
WJB_BEGIN_OBJECT, NULL);
|
||||
result->parseState->unique_keys = unique_keys;
|
||||
result->parseState->skip_nulls = absent_on_null;
|
||||
|
||||
MemoryContextSwitchTo(oldcontext);
|
||||
|
||||
arg_type = get_fn_expr_argtype(fcinfo->flinfo, 1);
|
||||
@ -1727,6 +1813,15 @@ jsonb_object_agg_transfn(PG_FUNCTION_ARGS)
|
||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||
errmsg("field name must not be null")));
|
||||
|
||||
/*
|
||||
* Skip null values if absent_on_null unless key uniqueness check is
|
||||
* needed (because we must save keys in this case).
|
||||
*/
|
||||
skip = absent_on_null && PG_ARGISNULL(2);
|
||||
|
||||
if (skip && !unique_keys)
|
||||
PG_RETURN_POINTER(state);
|
||||
|
||||
val = PG_GETARG_DATUM(1);
|
||||
|
||||
memset(&elem, 0, sizeof(JsonbInState));
|
||||
@ -1782,6 +1877,16 @@ jsonb_object_agg_transfn(PG_FUNCTION_ARGS)
|
||||
}
|
||||
result->res = pushJsonbValue(&result->parseState,
|
||||
WJB_KEY, &v);
|
||||
|
||||
if (skip)
|
||||
{
|
||||
v.type = jbvNull;
|
||||
result->res = pushJsonbValue(&result->parseState,
|
||||
WJB_VALUE, &v);
|
||||
MemoryContextSwitchTo(oldcontext);
|
||||
PG_RETURN_POINTER(state);
|
||||
}
|
||||
|
||||
break;
|
||||
case WJB_END_ARRAY:
|
||||
break;
|
||||
@ -1854,6 +1959,43 @@ jsonb_object_agg_transfn(PG_FUNCTION_ARGS)
|
||||
PG_RETURN_POINTER(state);
|
||||
}
|
||||
|
||||
/*
|
||||
* jsonb_object_agg aggregate function
|
||||
*/
|
||||
Datum
|
||||
jsonb_object_agg_transfn(PG_FUNCTION_ARGS)
|
||||
{
|
||||
return jsonb_object_agg_transfn_worker(fcinfo, false, false);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* jsonb_object_agg_strict aggregate function
|
||||
*/
|
||||
Datum
|
||||
jsonb_object_agg_strict_transfn(PG_FUNCTION_ARGS)
|
||||
{
|
||||
return jsonb_object_agg_transfn_worker(fcinfo, true, false);
|
||||
}
|
||||
|
||||
/*
|
||||
* jsonb_object_agg_unique aggregate function
|
||||
*/
|
||||
Datum
|
||||
jsonb_object_agg_unique_transfn(PG_FUNCTION_ARGS)
|
||||
{
|
||||
return jsonb_object_agg_transfn_worker(fcinfo, false, true);
|
||||
}
|
||||
|
||||
/*
|
||||
* jsonb_object_agg_unique_strict aggregate function
|
||||
*/
|
||||
Datum
|
||||
jsonb_object_agg_unique_strict_transfn(PG_FUNCTION_ARGS)
|
||||
{
|
||||
return jsonb_object_agg_transfn_worker(fcinfo, true, true);
|
||||
}
|
||||
|
||||
Datum
|
||||
jsonb_object_agg_finalfn(PG_FUNCTION_ARGS)
|
||||
{
|
||||
|
@ -64,7 +64,8 @@ static int lengthCompareJsonbStringValue(const void *a, const void *b);
|
||||
static int lengthCompareJsonbString(const char *val1, int len1,
|
||||
const char *val2, int len2);
|
||||
static int lengthCompareJsonbPair(const void *a, const void *b, void *arg);
|
||||
static void uniqueifyJsonbObject(JsonbValue *object);
|
||||
static void uniqueifyJsonbObject(JsonbValue *object, bool unique_keys,
|
||||
bool skip_nulls);
|
||||
static JsonbValue *pushJsonbValueScalar(JsonbParseState **pstate,
|
||||
JsonbIteratorToken seq,
|
||||
JsonbValue *scalarVal);
|
||||
@ -689,7 +690,9 @@ pushJsonbValueScalar(JsonbParseState **pstate, JsonbIteratorToken seq,
|
||||
appendElement(*pstate, scalarVal);
|
||||
break;
|
||||
case WJB_END_OBJECT:
|
||||
uniqueifyJsonbObject(&(*pstate)->contVal);
|
||||
uniqueifyJsonbObject(&(*pstate)->contVal,
|
||||
(*pstate)->unique_keys,
|
||||
(*pstate)->skip_nulls);
|
||||
/* fall through! */
|
||||
case WJB_END_ARRAY:
|
||||
/* Steps here common to WJB_END_OBJECT case */
|
||||
@ -732,6 +735,9 @@ pushState(JsonbParseState **pstate)
|
||||
JsonbParseState *ns = palloc(sizeof(JsonbParseState));
|
||||
|
||||
ns->next = *pstate;
|
||||
ns->unique_keys = false;
|
||||
ns->skip_nulls = false;
|
||||
|
||||
return ns;
|
||||
}
|
||||
|
||||
@ -1936,7 +1942,7 @@ lengthCompareJsonbPair(const void *a, const void *b, void *binequal)
|
||||
* Sort and unique-ify pairs in JsonbValue object
|
||||
*/
|
||||
static void
|
||||
uniqueifyJsonbObject(JsonbValue *object)
|
||||
uniqueifyJsonbObject(JsonbValue *object, bool unique_keys, bool skip_nulls)
|
||||
{
|
||||
bool hasNonUniq = false;
|
||||
|
||||
@ -1946,15 +1952,21 @@ uniqueifyJsonbObject(JsonbValue *object)
|
||||
qsort_arg(object->val.object.pairs, object->val.object.nPairs, sizeof(JsonbPair),
|
||||
lengthCompareJsonbPair, &hasNonUniq);
|
||||
|
||||
if (hasNonUniq)
|
||||
if (hasNonUniq && unique_keys)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_DUPLICATE_JSON_OBJECT_KEY_VALUE),
|
||||
errmsg("duplicate JSON object key value")));
|
||||
|
||||
if (hasNonUniq || skip_nulls)
|
||||
{
|
||||
JsonbPair *ptr = object->val.object.pairs + 1,
|
||||
*res = object->val.object.pairs;
|
||||
|
||||
while (ptr - object->val.object.pairs < object->val.object.nPairs)
|
||||
{
|
||||
/* Avoid copying over duplicate */
|
||||
if (lengthCompareJsonbStringValue(ptr, res) != 0)
|
||||
/* Avoid copying over duplicate or null */
|
||||
if (lengthCompareJsonbStringValue(ptr, res) != 0 &&
|
||||
(!skip_nulls || ptr->value.type != jbvNull))
|
||||
{
|
||||
res++;
|
||||
if (ptr != res)
|
||||
|
@ -457,6 +457,12 @@ static void get_coercion_expr(Node *arg, deparse_context *context,
|
||||
Node *parentNode);
|
||||
static void get_const_expr(Const *constval, deparse_context *context,
|
||||
int showtype);
|
||||
static void get_json_constructor(JsonConstructorExpr *ctor,
|
||||
deparse_context *context, bool showimplicit);
|
||||
static void get_json_agg_constructor(JsonConstructorExpr *ctor,
|
||||
deparse_context *context,
|
||||
const char *funcname,
|
||||
bool is_json_objectagg);
|
||||
static void get_const_collation(Const *constval, deparse_context *context);
|
||||
static void simple_quote_literal(StringInfo buf, const char *val);
|
||||
static void get_sublink_expr(SubLink *sublink, deparse_context *context);
|
||||
@ -6245,7 +6251,8 @@ get_rule_sortgroupclause(Index ref, List *tlist, bool force_colno,
|
||||
bool need_paren = (PRETTY_PAREN(context)
|
||||
|| IsA(expr, FuncExpr)
|
||||
|| IsA(expr, Aggref)
|
||||
|| IsA(expr, WindowFunc));
|
||||
|| IsA(expr, WindowFunc)
|
||||
|| IsA(expr, JsonConstructorExpr));
|
||||
|
||||
if (need_paren)
|
||||
appendStringInfoChar(context->buf, '(');
|
||||
@ -8093,6 +8100,7 @@ isSimpleNode(Node *node, Node *parentNode, int prettyFlags)
|
||||
case T_GroupingFunc:
|
||||
case T_WindowFunc:
|
||||
case T_FuncExpr:
|
||||
case T_JsonConstructorExpr:
|
||||
/* function-like: name(..) or name[..] */
|
||||
return true;
|
||||
|
||||
@ -8380,12 +8388,12 @@ get_rule_expr_paren(Node *node, deparse_context *context,
|
||||
* get_json_format - Parse back a JsonFormat node
|
||||
*/
|
||||
static void
|
||||
get_json_format(JsonFormat *format, deparse_context *context)
|
||||
get_json_format(JsonFormat *format, StringInfo buf)
|
||||
{
|
||||
if (format->format_type == JS_FORMAT_DEFAULT)
|
||||
return;
|
||||
|
||||
appendStringInfoString(context->buf,
|
||||
appendStringInfoString(buf,
|
||||
format->format_type == JS_FORMAT_JSONB ?
|
||||
" FORMAT JSONB" : " FORMAT JSON");
|
||||
|
||||
@ -8395,7 +8403,7 @@ get_json_format(JsonFormat *format, deparse_context *context)
|
||||
format->encoding == JS_ENC_UTF16 ? "UTF16" :
|
||||
format->encoding == JS_ENC_UTF32 ? "UTF32" : "UTF8";
|
||||
|
||||
appendStringInfo(context->buf, " ENCODING %s", encoding);
|
||||
appendStringInfo(buf, " ENCODING %s", encoding);
|
||||
}
|
||||
}
|
||||
|
||||
@ -8403,20 +8411,20 @@ get_json_format(JsonFormat *format, deparse_context *context)
|
||||
* get_json_returning - Parse back a JsonReturning structure
|
||||
*/
|
||||
static void
|
||||
get_json_returning(JsonReturning *returning, deparse_context *context,
|
||||
get_json_returning(JsonReturning *returning, StringInfo buf,
|
||||
bool json_format_by_default)
|
||||
{
|
||||
if (!OidIsValid(returning->typid))
|
||||
return;
|
||||
|
||||
appendStringInfo(context->buf, " RETURNING %s",
|
||||
appendStringInfo(buf, " RETURNING %s",
|
||||
format_type_with_typemod(returning->typid,
|
||||
returning->typmod));
|
||||
|
||||
if (!json_format_by_default ||
|
||||
returning->format->format_type !=
|
||||
(returning->typid == JSONBOID ? JS_FORMAT_JSONB : JS_FORMAT_JSON))
|
||||
get_json_format(returning->format, context);
|
||||
get_json_format(returning->format, buf);
|
||||
}
|
||||
|
||||
/* ----------
|
||||
@ -9583,10 +9591,14 @@ get_rule_expr(Node *node, deparse_context *context,
|
||||
JsonValueExpr *jve = (JsonValueExpr *) node;
|
||||
|
||||
get_rule_expr((Node *) jve->raw_expr, context, false);
|
||||
get_json_format(jve->format, context);
|
||||
get_json_format(jve->format, context->buf);
|
||||
}
|
||||
break;
|
||||
|
||||
case T_JsonConstructorExpr:
|
||||
get_json_constructor((JsonConstructorExpr *) node, context, false);
|
||||
break;
|
||||
|
||||
case T_List:
|
||||
{
|
||||
char *sep;
|
||||
@ -9855,17 +9867,89 @@ get_func_expr(FuncExpr *expr, deparse_context *context,
|
||||
appendStringInfoChar(buf, ')');
|
||||
}
|
||||
|
||||
static void
|
||||
get_json_constructor_options(JsonConstructorExpr *ctor, StringInfo buf)
|
||||
{
|
||||
if (ctor->absent_on_null)
|
||||
{
|
||||
if (ctor->type == JSCTOR_JSON_OBJECT ||
|
||||
ctor->type == JSCTOR_JSON_OBJECTAGG)
|
||||
appendStringInfoString(buf, " ABSENT ON NULL");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ctor->type == JSCTOR_JSON_ARRAY ||
|
||||
ctor->type == JSCTOR_JSON_ARRAYAGG)
|
||||
appendStringInfoString(buf, " NULL ON NULL");
|
||||
}
|
||||
|
||||
if (ctor->unique)
|
||||
appendStringInfoString(buf, " WITH UNIQUE KEYS");
|
||||
|
||||
get_json_returning(ctor->returning, buf, true);
|
||||
}
|
||||
|
||||
static void
|
||||
get_json_constructor(JsonConstructorExpr *ctor, deparse_context *context,
|
||||
bool showimplicit)
|
||||
{
|
||||
StringInfo buf = context->buf;
|
||||
const char *funcname;
|
||||
int nargs;
|
||||
ListCell *lc;
|
||||
|
||||
switch (ctor->type)
|
||||
{
|
||||
case JSCTOR_JSON_OBJECT:
|
||||
funcname = "JSON_OBJECT";
|
||||
break;
|
||||
case JSCTOR_JSON_ARRAY:
|
||||
funcname = "JSON_ARRAY";
|
||||
break;
|
||||
case JSCTOR_JSON_OBJECTAGG:
|
||||
return get_json_agg_constructor(ctor, context, "JSON_OBJECTAGG", true);
|
||||
case JSCTOR_JSON_ARRAYAGG:
|
||||
return get_json_agg_constructor(ctor, context, "JSON_ARRAYAGG", false);
|
||||
default:
|
||||
elog(ERROR, "invalid JsonConstructorExprType %d", ctor->type);
|
||||
}
|
||||
|
||||
appendStringInfo(buf, "%s(", funcname);
|
||||
|
||||
nargs = 0;
|
||||
foreach(lc, ctor->args)
|
||||
{
|
||||
if (nargs > 0)
|
||||
{
|
||||
const char *sep = ctor->type == JSCTOR_JSON_OBJECT &&
|
||||
(nargs % 2) != 0 ? " : " : ", ";
|
||||
|
||||
appendStringInfoString(buf, sep);
|
||||
}
|
||||
|
||||
get_rule_expr((Node *) lfirst(lc), context, true);
|
||||
|
||||
nargs++;
|
||||
}
|
||||
|
||||
get_json_constructor_options(ctor, buf);
|
||||
|
||||
appendStringInfo(buf, ")");
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* get_agg_expr - Parse back an Aggref node
|
||||
* get_agg_expr_helper - Parse back an Aggref node
|
||||
*/
|
||||
static void
|
||||
get_agg_expr(Aggref *aggref, deparse_context *context,
|
||||
Aggref *original_aggref)
|
||||
get_agg_expr_helper(Aggref *aggref, deparse_context *context,
|
||||
Aggref *original_aggref, const char *funcname,
|
||||
const char *options, bool is_json_objectagg)
|
||||
{
|
||||
StringInfo buf = context->buf;
|
||||
Oid argtypes[FUNC_MAX_ARGS];
|
||||
int nargs;
|
||||
bool use_variadic;
|
||||
bool use_variadic = false;
|
||||
|
||||
/*
|
||||
* For a combining aggregate, we look up and deparse the corresponding
|
||||
@ -9895,13 +9979,14 @@ get_agg_expr(Aggref *aggref, deparse_context *context,
|
||||
/* Extract the argument types as seen by the parser */
|
||||
nargs = get_aggregate_argtypes(aggref, argtypes);
|
||||
|
||||
/* Print the aggregate name, schema-qualified if needed */
|
||||
appendStringInfo(buf, "%s(%s",
|
||||
generate_function_name(aggref->aggfnoid, nargs,
|
||||
NIL, argtypes,
|
||||
aggref->aggvariadic,
|
||||
if (!funcname)
|
||||
funcname = generate_function_name(aggref->aggfnoid, nargs, NIL,
|
||||
argtypes, aggref->aggvariadic,
|
||||
&use_variadic,
|
||||
context->special_exprkind),
|
||||
context->special_exprkind);
|
||||
|
||||
/* Print the aggregate name, schema-qualified if needed */
|
||||
appendStringInfo(buf, "%s(%s", funcname,
|
||||
(aggref->aggdistinct != NIL) ? "DISTINCT " : "");
|
||||
|
||||
if (AGGKIND_IS_ORDERED_SET(aggref->aggkind))
|
||||
@ -9937,7 +10022,17 @@ get_agg_expr(Aggref *aggref, deparse_context *context,
|
||||
if (tle->resjunk)
|
||||
continue;
|
||||
if (i++ > 0)
|
||||
{
|
||||
if (is_json_objectagg)
|
||||
{
|
||||
if (i > 2)
|
||||
break; /* skip ABSENT ON NULL and WITH UNIQUE args */
|
||||
|
||||
appendStringInfoString(buf, " : ");
|
||||
}
|
||||
else
|
||||
appendStringInfoString(buf, ", ");
|
||||
}
|
||||
if (use_variadic && i == nargs)
|
||||
appendStringInfoString(buf, "VARIADIC ");
|
||||
get_rule_expr(arg, context, true);
|
||||
@ -9951,6 +10046,9 @@ get_agg_expr(Aggref *aggref, deparse_context *context,
|
||||
}
|
||||
}
|
||||
|
||||
if (options)
|
||||
appendStringInfoString(buf, options);
|
||||
|
||||
if (aggref->aggfilter != NULL)
|
||||
{
|
||||
appendStringInfoString(buf, ") FILTER (WHERE ");
|
||||
@ -9960,6 +10058,16 @@ get_agg_expr(Aggref *aggref, deparse_context *context,
|
||||
appendStringInfoChar(buf, ')');
|
||||
}
|
||||
|
||||
/*
|
||||
* get_agg_expr - Parse back an Aggref node
|
||||
*/
|
||||
static void
|
||||
get_agg_expr(Aggref *aggref, deparse_context *context, Aggref *original_aggref)
|
||||
{
|
||||
return get_agg_expr_helper(aggref, context, original_aggref, NULL, NULL,
|
||||
false);
|
||||
}
|
||||
|
||||
/*
|
||||
* This is a helper function for get_agg_expr(). It's used when we deparse
|
||||
* a combining Aggref; resolve_special_varno locates the corresponding partial
|
||||
@ -9979,10 +10087,12 @@ get_agg_combine_expr(Node *node, deparse_context *context, void *callback_arg)
|
||||
}
|
||||
|
||||
/*
|
||||
* get_windowfunc_expr - Parse back a WindowFunc node
|
||||
* get_windowfunc_expr_helper - Parse back a WindowFunc node
|
||||
*/
|
||||
static void
|
||||
get_windowfunc_expr(WindowFunc *wfunc, deparse_context *context)
|
||||
get_windowfunc_expr_helper(WindowFunc *wfunc, deparse_context *context,
|
||||
const char *funcname, const char *options,
|
||||
bool is_json_objectagg)
|
||||
{
|
||||
StringInfo buf = context->buf;
|
||||
Oid argtypes[FUNC_MAX_ARGS];
|
||||
@ -10006,16 +10116,30 @@ get_windowfunc_expr(WindowFunc *wfunc, deparse_context *context)
|
||||
nargs++;
|
||||
}
|
||||
|
||||
appendStringInfo(buf, "%s(",
|
||||
generate_function_name(wfunc->winfnoid, nargs,
|
||||
argnames, argtypes,
|
||||
false, NULL,
|
||||
context->special_exprkind));
|
||||
if (!funcname)
|
||||
funcname = generate_function_name(wfunc->winfnoid, nargs, argnames,
|
||||
argtypes, false, NULL,
|
||||
context->special_exprkind);
|
||||
|
||||
appendStringInfo(buf, "%s(", funcname);
|
||||
|
||||
/* winstar can be set only in zero-argument aggregates */
|
||||
if (wfunc->winstar)
|
||||
appendStringInfoChar(buf, '*');
|
||||
else
|
||||
{
|
||||
if (is_json_objectagg)
|
||||
{
|
||||
get_rule_expr((Node *) linitial(wfunc->args), context, false);
|
||||
appendStringInfoString(buf, " : ");
|
||||
get_rule_expr((Node *) lsecond(wfunc->args), context, false);
|
||||
}
|
||||
else
|
||||
get_rule_expr((Node *) wfunc->args, context, true);
|
||||
}
|
||||
|
||||
if (options)
|
||||
appendStringInfoString(buf, options);
|
||||
|
||||
if (wfunc->aggfilter != NULL)
|
||||
{
|
||||
@ -10052,6 +10176,15 @@ get_windowfunc_expr(WindowFunc *wfunc, deparse_context *context)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* get_windowfunc_expr - Parse back a WindowFunc node
|
||||
*/
|
||||
static void
|
||||
get_windowfunc_expr(WindowFunc *wfunc, deparse_context *context)
|
||||
{
|
||||
return get_windowfunc_expr_helper(wfunc, context, NULL, NULL, false);
|
||||
}
|
||||
|
||||
/*
|
||||
* get_func_sql_syntax - Parse back a SQL-syntax function call
|
||||
*
|
||||
@ -10292,6 +10425,31 @@ get_func_sql_syntax(FuncExpr *expr, deparse_context *context)
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* get_json_agg_constructor - Parse back an aggregate JsonConstructorExpr node
|
||||
*/
|
||||
static void
|
||||
get_json_agg_constructor(JsonConstructorExpr *ctor, deparse_context *context,
|
||||
const char *funcname, bool is_json_objectagg)
|
||||
{
|
||||
StringInfoData options;
|
||||
|
||||
initStringInfo(&options);
|
||||
get_json_constructor_options(ctor, &options);
|
||||
|
||||
if (IsA(ctor->func, Aggref))
|
||||
return get_agg_expr_helper((Aggref *) ctor->func, context,
|
||||
(Aggref *) ctor->func,
|
||||
funcname, options.data, is_json_objectagg);
|
||||
else if (IsA(ctor->func, WindowFunc))
|
||||
return get_windowfunc_expr_helper((WindowFunc *) ctor->func, context,
|
||||
funcname, options.data,
|
||||
is_json_objectagg);
|
||||
else
|
||||
elog(ERROR, "invalid JsonConstructorExpr underlying node type: %d",
|
||||
nodeTag(ctor->func));
|
||||
}
|
||||
|
||||
/* ----------
|
||||
* get_coercion_expr
|
||||
*
|
||||
|
@ -763,6 +763,18 @@ JumbleExpr(JumbleState *jstate, Node *node)
|
||||
JumbleExpr(jstate, (Node *) expr->format);
|
||||
}
|
||||
break;
|
||||
case T_JsonConstructorExpr:
|
||||
{
|
||||
JsonConstructorExpr *ctor = (JsonConstructorExpr *) node;
|
||||
|
||||
JumbleExpr(jstate, (Node *) ctor->func);
|
||||
JumbleExpr(jstate, (Node *) ctor->coercion);
|
||||
JumbleExpr(jstate, (Node *) ctor->returning);
|
||||
APP_JUMB(ctor->type);
|
||||
APP_JUMB(ctor->unique);
|
||||
APP_JUMB(ctor->absent_on_null);
|
||||
}
|
||||
break;
|
||||
case T_List:
|
||||
foreach(temp, (List *) node)
|
||||
{
|
||||
|
@ -567,14 +567,36 @@
|
||||
# json
|
||||
{ aggfnoid => 'json_agg', aggtransfn => 'json_agg_transfn',
|
||||
aggfinalfn => 'json_agg_finalfn', aggtranstype => 'internal' },
|
||||
{ aggfnoid => 'json_agg_strict', aggtransfn => 'json_agg_strict_transfn',
|
||||
aggfinalfn => 'json_agg_finalfn', aggtranstype => 'internal' },
|
||||
{ aggfnoid => 'json_object_agg', aggtransfn => 'json_object_agg_transfn',
|
||||
aggfinalfn => 'json_object_agg_finalfn', aggtranstype => 'internal' },
|
||||
{ aggfnoid => 'json_object_agg_unique',
|
||||
aggtransfn => 'json_object_agg_unique_transfn',
|
||||
aggfinalfn => 'json_object_agg_finalfn', aggtranstype => 'internal' },
|
||||
{ aggfnoid => 'json_object_agg_strict',
|
||||
aggtransfn => 'json_object_agg_strict_transfn',
|
||||
aggfinalfn => 'json_object_agg_finalfn', aggtranstype => 'internal' },
|
||||
{ aggfnoid => 'json_object_agg_unique_strict',
|
||||
aggtransfn => 'json_object_agg_unique_strict_transfn',
|
||||
aggfinalfn => 'json_object_agg_finalfn', aggtranstype => 'internal' },
|
||||
|
||||
# jsonb
|
||||
{ aggfnoid => 'jsonb_agg', aggtransfn => 'jsonb_agg_transfn',
|
||||
aggfinalfn => 'jsonb_agg_finalfn', aggtranstype => 'internal' },
|
||||
{ aggfnoid => 'jsonb_agg_strict', aggtransfn => 'jsonb_agg_strict_transfn',
|
||||
aggfinalfn => 'jsonb_agg_finalfn', aggtranstype => 'internal' },
|
||||
{ aggfnoid => 'jsonb_object_agg', aggtransfn => 'jsonb_object_agg_transfn',
|
||||
aggfinalfn => 'jsonb_object_agg_finalfn', aggtranstype => 'internal' },
|
||||
{ aggfnoid => 'jsonb_object_agg_unique',
|
||||
aggtransfn => 'jsonb_object_agg_unique_transfn',
|
||||
aggfinalfn => 'jsonb_object_agg_finalfn', aggtranstype => 'internal' },
|
||||
{ aggfnoid => 'jsonb_object_agg_strict',
|
||||
aggtransfn => 'jsonb_object_agg_strict_transfn',
|
||||
aggfinalfn => 'jsonb_object_agg_finalfn', aggtranstype => 'internal' },
|
||||
{ aggfnoid => 'jsonb_object_agg_unique_strict',
|
||||
aggtransfn => 'jsonb_object_agg_unique_strict_transfn',
|
||||
aggfinalfn => 'jsonb_object_agg_finalfn', aggtranstype => 'internal' },
|
||||
|
||||
# ordered-set and hypothetical-set aggregates
|
||||
{ aggfnoid => 'percentile_disc(float8,anyelement)', aggkind => 'o',
|
||||
|
@ -8737,6 +8737,10 @@
|
||||
proname => 'json_agg_transfn', proisstrict => 'f', provolatile => 's',
|
||||
prorettype => 'internal', proargtypes => 'internal anyelement',
|
||||
prosrc => 'json_agg_transfn' },
|
||||
{ oid => '8173', descr => 'json aggregate transition function',
|
||||
proname => 'json_agg_strict_transfn', proisstrict => 'f', provolatile => 's',
|
||||
prorettype => 'internal', proargtypes => 'internal anyelement',
|
||||
prosrc => 'json_agg_strict_transfn' },
|
||||
{ oid => '3174', descr => 'json aggregate final function',
|
||||
proname => 'json_agg_finalfn', proisstrict => 'f', prorettype => 'json',
|
||||
proargtypes => 'internal', prosrc => 'json_agg_finalfn' },
|
||||
@ -8744,10 +8748,26 @@
|
||||
proname => 'json_agg', prokind => 'a', proisstrict => 'f', provolatile => 's',
|
||||
prorettype => 'json', proargtypes => 'anyelement',
|
||||
prosrc => 'aggregate_dummy' },
|
||||
{ oid => '8174', descr => 'aggregate input into json',
|
||||
proname => 'json_agg_strict', prokind => 'a', proisstrict => 'f',
|
||||
provolatile => 's', prorettype => 'json', proargtypes => 'anyelement',
|
||||
prosrc => 'aggregate_dummy' },
|
||||
{ oid => '3180', descr => 'json object aggregate transition function',
|
||||
proname => 'json_object_agg_transfn', proisstrict => 'f', provolatile => 's',
|
||||
prorettype => 'internal', proargtypes => 'internal any any',
|
||||
prosrc => 'json_object_agg_transfn' },
|
||||
{ oid => '8175', descr => 'json object aggregate transition function',
|
||||
proname => 'json_object_agg_strict_transfn', proisstrict => 'f',
|
||||
provolatile => 's', prorettype => 'internal', proargtypes => 'internal any any',
|
||||
prosrc => 'json_object_agg_strict_transfn' },
|
||||
{ oid => '8176', descr => 'json object aggregate transition function',
|
||||
proname => 'json_object_agg_unique_transfn', proisstrict => 'f',
|
||||
provolatile => 's', prorettype => 'internal', proargtypes => 'internal any any',
|
||||
prosrc => 'json_object_agg_unique_transfn' },
|
||||
{ oid => '8177', descr => 'json object aggregate transition function',
|
||||
proname => 'json_object_agg_unique_strict_transfn', proisstrict => 'f',
|
||||
provolatile => 's', prorettype => 'internal', proargtypes => 'internal any any',
|
||||
prosrc => 'json_object_agg_unique_strict_transfn' },
|
||||
{ oid => '3196', descr => 'json object aggregate final function',
|
||||
proname => 'json_object_agg_finalfn', proisstrict => 'f',
|
||||
prorettype => 'json', proargtypes => 'internal',
|
||||
@ -8756,6 +8776,19 @@
|
||||
proname => 'json_object_agg', prokind => 'a', proisstrict => 'f',
|
||||
provolatile => 's', prorettype => 'json', proargtypes => 'any any',
|
||||
prosrc => 'aggregate_dummy' },
|
||||
{ oid => '8178', descr => 'aggregate non-NULL input into a json object',
|
||||
proname => 'json_object_agg_strict', prokind => 'a', proisstrict => 'f',
|
||||
provolatile => 's', prorettype => 'json', proargtypes => 'any any',
|
||||
prosrc => 'aggregate_dummy' },
|
||||
{ oid => '8179', descr => 'aggregate input into a json object with unique keys',
|
||||
proname => 'json_object_agg_unique', prokind => 'a', proisstrict => 'f',
|
||||
provolatile => 's', prorettype => 'json', proargtypes => 'any any',
|
||||
prosrc => 'aggregate_dummy' },
|
||||
{ oid => '8180',
|
||||
descr => 'aggregate non-NULL input into a json object with unique keys',
|
||||
proname => 'json_object_agg_unique_strict', prokind => 'a',
|
||||
proisstrict => 'f', provolatile => 's', prorettype => 'json',
|
||||
proargtypes => 'any any', prosrc => 'aggregate_dummy' },
|
||||
{ oid => '3198', descr => 'build a json array from any inputs',
|
||||
proname => 'json_build_array', provariadic => 'any', proisstrict => 'f',
|
||||
provolatile => 's', prorettype => 'json', proargtypes => 'any',
|
||||
@ -9628,6 +9661,10 @@
|
||||
proname => 'jsonb_agg_transfn', proisstrict => 'f', provolatile => 's',
|
||||
prorettype => 'internal', proargtypes => 'internal anyelement',
|
||||
prosrc => 'jsonb_agg_transfn' },
|
||||
{ oid => '8181', descr => 'jsonb aggregate transition function',
|
||||
proname => 'jsonb_agg_strict_transfn', proisstrict => 'f', provolatile => 's',
|
||||
prorettype => 'internal', proargtypes => 'internal anyelement',
|
||||
prosrc => 'jsonb_agg_strict_transfn' },
|
||||
{ oid => '3266', descr => 'jsonb aggregate final function',
|
||||
proname => 'jsonb_agg_finalfn', proisstrict => 'f', provolatile => 's',
|
||||
prorettype => 'jsonb', proargtypes => 'internal',
|
||||
@ -9636,10 +9673,29 @@
|
||||
proname => 'jsonb_agg', prokind => 'a', proisstrict => 'f',
|
||||
provolatile => 's', prorettype => 'jsonb', proargtypes => 'anyelement',
|
||||
prosrc => 'aggregate_dummy' },
|
||||
{ oid => '8182', descr => 'aggregate input into jsonb skipping nulls',
|
||||
proname => 'jsonb_agg_strict', prokind => 'a', proisstrict => 'f',
|
||||
provolatile => 's', prorettype => 'jsonb', proargtypes => 'anyelement',
|
||||
prosrc => 'aggregate_dummy' },
|
||||
{ oid => '3268', descr => 'jsonb object aggregate transition function',
|
||||
proname => 'jsonb_object_agg_transfn', proisstrict => 'f', provolatile => 's',
|
||||
prorettype => 'internal', proargtypes => 'internal any any',
|
||||
prosrc => 'jsonb_object_agg_transfn' },
|
||||
{ oid => '8183', descr => 'jsonb object aggregate transition function',
|
||||
proname => 'jsonb_object_agg_strict_transfn', proisstrict => 'f',
|
||||
provolatile => 's', prorettype => 'internal',
|
||||
proargtypes => 'internal any any',
|
||||
prosrc => 'jsonb_object_agg_strict_transfn' },
|
||||
{ oid => '8184', descr => 'jsonb object aggregate transition function',
|
||||
proname => 'jsonb_object_agg_unique_transfn', proisstrict => 'f',
|
||||
provolatile => 's', prorettype => 'internal',
|
||||
proargtypes => 'internal any any',
|
||||
prosrc => 'jsonb_object_agg_unique_transfn' },
|
||||
{ oid => '8185', descr => 'jsonb object aggregate transition function',
|
||||
proname => 'jsonb_object_agg_unique_strict_transfn', proisstrict => 'f',
|
||||
provolatile => 's', prorettype => 'internal',
|
||||
proargtypes => 'internal any any',
|
||||
prosrc => 'jsonb_object_agg_unique_strict_transfn' },
|
||||
{ oid => '3269', descr => 'jsonb object aggregate final function',
|
||||
proname => 'jsonb_object_agg_finalfn', proisstrict => 'f', provolatile => 's',
|
||||
prorettype => 'jsonb', proargtypes => 'internal',
|
||||
@ -9648,6 +9704,20 @@
|
||||
proname => 'jsonb_object_agg', prokind => 'a', proisstrict => 'f',
|
||||
prorettype => 'jsonb', proargtypes => 'any any',
|
||||
prosrc => 'aggregate_dummy' },
|
||||
{ oid => '8186', descr => 'aggregate non-NULL inputs into jsonb object',
|
||||
proname => 'jsonb_object_agg_strict', prokind => 'a', proisstrict => 'f',
|
||||
prorettype => 'jsonb', proargtypes => 'any any',
|
||||
prosrc => 'aggregate_dummy' },
|
||||
{ oid => '8187',
|
||||
descr => 'aggregate inputs into jsonb object checking key uniqueness',
|
||||
proname => 'jsonb_object_agg_unique', prokind => 'a', proisstrict => 'f',
|
||||
prorettype => 'jsonb', proargtypes => 'any any',
|
||||
prosrc => 'aggregate_dummy' },
|
||||
{ oid => '8188',
|
||||
descr => 'aggregate non-NULL inputs into jsonb object checking key uniqueness',
|
||||
proname => 'jsonb_object_agg_unique_strict', prokind => 'a',
|
||||
proisstrict => 'f', prorettype => 'jsonb', proargtypes => 'any any',
|
||||
prosrc => 'aggregate_dummy' },
|
||||
{ oid => '3271', descr => 'build a jsonb array from any inputs',
|
||||
proname => 'jsonb_build_array', provariadic => 'any', proisstrict => 'f',
|
||||
provolatile => 's', prorettype => 'jsonb', proargtypes => 'any',
|
||||
|
@ -239,6 +239,7 @@ typedef enum ExprEvalOp
|
||||
EEOP_GROUPING_FUNC,
|
||||
EEOP_WINDOW_FUNC,
|
||||
EEOP_SUBPLAN,
|
||||
EEOP_JSON_CONSTRUCTOR,
|
||||
|
||||
/* aggregation related nodes */
|
||||
EEOP_AGG_STRICT_DESERIALIZE,
|
||||
@ -668,6 +669,17 @@ typedef struct ExprEvalStep
|
||||
int transno;
|
||||
int setoff;
|
||||
} agg_trans;
|
||||
|
||||
/* for EEOP_JSON_CONSTRUCTOR */
|
||||
struct
|
||||
{
|
||||
JsonConstructorExpr *constructor;
|
||||
Datum *arg_values;
|
||||
bool *arg_nulls;
|
||||
Oid *arg_types;
|
||||
int nargs;
|
||||
} json_constructor;
|
||||
|
||||
} d;
|
||||
} ExprEvalStep;
|
||||
|
||||
@ -769,6 +781,8 @@ extern void ExecEvalWholeRowVar(ExprState *state, ExprEvalStep *op,
|
||||
ExprContext *econtext);
|
||||
extern void ExecEvalSysVar(ExprState *state, ExprEvalStep *op,
|
||||
ExprContext *econtext, TupleTableSlot *slot);
|
||||
extern void ExecEvalJsonConstructor(ExprState *state, ExprEvalStep *op,
|
||||
ExprContext *econtext);
|
||||
|
||||
extern void ExecAggInitGroup(AggState *aggstate, AggStatePerTrans pertrans, AggStatePerGroup pergroup,
|
||||
ExprContext *aggcontext);
|
||||
|
@ -109,6 +109,7 @@ extern VacuumRelation *makeVacuumRelation(RangeVar *relation, Oid oid, List *va_
|
||||
extern JsonFormat *makeJsonFormat(JsonFormatType type, JsonEncoding encoding,
|
||||
int location);
|
||||
extern JsonValueExpr *makeJsonValueExpr(Expr *expr, JsonFormat *format);
|
||||
extern Node *makeJsonKeyValue(Node *key, Node *value);
|
||||
extern JsonEncoding makeJsonEncoding(char *name);
|
||||
|
||||
#endif /* MAKEFUNC_H */
|
||||
|
@ -204,6 +204,7 @@ typedef enum NodeTag
|
||||
T_JsonFormat,
|
||||
T_JsonReturning,
|
||||
T_JsonValueExpr,
|
||||
T_JsonConstructorExpr,
|
||||
|
||||
/*
|
||||
* TAGS FOR EXPRESSION STATE NODES (execnodes.h)
|
||||
@ -494,6 +495,13 @@ typedef enum NodeTag
|
||||
T_VacuumRelation,
|
||||
T_PublicationObjSpec,
|
||||
T_PublicationTable,
|
||||
T_JsonObjectConstructor,
|
||||
T_JsonArrayConstructor,
|
||||
T_JsonArrayQueryConstructor,
|
||||
T_JsonAggConstructor,
|
||||
T_JsonObjectAgg,
|
||||
T_JsonArrayAgg,
|
||||
T_JsonKeyValue,
|
||||
T_JsonOutput,
|
||||
|
||||
/*
|
||||
|
@ -1563,9 +1563,103 @@ typedef struct JsonOutput
|
||||
{
|
||||
NodeTag type;
|
||||
TypeName *typeName; /* RETURNING type name, if specified */
|
||||
JsonReturning returning; /* RETURNING FORMAT clause and type Oids */
|
||||
JsonReturning *returning; /* RETURNING FORMAT clause and type Oids */
|
||||
} JsonOutput;
|
||||
|
||||
/*
|
||||
* JsonKeyValue -
|
||||
* untransformed representation of JSON object key-value pair for
|
||||
* JSON_OBJECT() and JSON_OBJECTAGG()
|
||||
*/
|
||||
typedef struct JsonKeyValue
|
||||
{
|
||||
NodeTag type;
|
||||
Expr *key; /* key expression */
|
||||
JsonValueExpr *value; /* JSON value expression */
|
||||
} JsonKeyValue;
|
||||
|
||||
/*
|
||||
* JsonObjectConstructor -
|
||||
* untransformed representation of JSON_OBJECT() constructor
|
||||
*/
|
||||
typedef struct JsonObjectConstructor
|
||||
{
|
||||
NodeTag type;
|
||||
List *exprs; /* list of JsonKeyValue pairs */
|
||||
JsonOutput *output; /* RETURNING clause, if specified */
|
||||
bool absent_on_null; /* skip NULL values? */
|
||||
bool unique; /* check key uniqueness? */
|
||||
int location; /* token location, or -1 if unknown */
|
||||
} JsonObjectConstructor;
|
||||
|
||||
/*
|
||||
* JsonArrayConstructor -
|
||||
* untransformed representation of JSON_ARRAY(element,...) constructor
|
||||
*/
|
||||
typedef struct JsonArrayConstructor
|
||||
{
|
||||
NodeTag type;
|
||||
List *exprs; /* list of JsonValueExpr elements */
|
||||
JsonOutput *output; /* RETURNING clause, if specified */
|
||||
bool absent_on_null; /* skip NULL elements? */
|
||||
int location; /* token location, or -1 if unknown */
|
||||
} JsonArrayConstructor;
|
||||
|
||||
/*
|
||||
* JsonArrayQueryConstructor -
|
||||
* untransformed representation of JSON_ARRAY(subquery) constructor
|
||||
*/
|
||||
typedef struct JsonArrayQueryConstructor
|
||||
{
|
||||
NodeTag type;
|
||||
Node *query; /* subquery */
|
||||
JsonOutput *output; /* RETURNING clause, if specified */
|
||||
JsonFormat *format; /* FORMAT clause for subquery, if specified */
|
||||
bool absent_on_null; /* skip NULL elements? */
|
||||
int location; /* token location, or -1 if unknown */
|
||||
} JsonArrayQueryConstructor;
|
||||
|
||||
/*
|
||||
* JsonAggConstructor -
|
||||
* common fields of untransformed representation of
|
||||
* JSON_ARRAYAGG() and JSON_OBJECTAGG()
|
||||
*/
|
||||
typedef struct JsonAggConstructor
|
||||
{
|
||||
NodeTag type;
|
||||
JsonOutput *output; /* RETURNING clause, if any */
|
||||
Node *agg_filter; /* FILTER clause, if any */
|
||||
List *agg_order; /* ORDER BY clause, if any */
|
||||
struct WindowDef *over; /* OVER clause, if any */
|
||||
int location; /* token location, or -1 if unknown */
|
||||
} JsonAggConstructor;
|
||||
|
||||
/*
|
||||
* JsonObjectAgg -
|
||||
* untransformed representation of JSON_OBJECTAGG()
|
||||
*/
|
||||
typedef struct JsonObjectAgg
|
||||
{
|
||||
NodeTag type;
|
||||
JsonAggConstructor *constructor; /* common fields */
|
||||
JsonKeyValue *arg; /* object key-value pair */
|
||||
bool absent_on_null; /* skip NULL values? */
|
||||
bool unique; /* check key uniqueness? */
|
||||
} JsonObjectAgg;
|
||||
|
||||
/*
|
||||
* JsonArrayAgg -
|
||||
* untransformed representation of JSON_ARRRAYAGG()
|
||||
*/
|
||||
typedef struct JsonArrayAgg
|
||||
{
|
||||
NodeTag type;
|
||||
JsonAggConstructor *constructor; /* common fields */
|
||||
JsonValueExpr *arg; /* array element expression */
|
||||
bool absent_on_null; /* skip NULL elements? */
|
||||
} JsonArrayAgg;
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* Raw Grammar Output Statements
|
||||
*****************************************************************************/
|
||||
|
@ -1292,6 +1292,31 @@ typedef struct JsonValueExpr
|
||||
JsonFormat *format; /* FORMAT clause, if specified */
|
||||
} JsonValueExpr;
|
||||
|
||||
typedef enum JsonConstructorType
|
||||
{
|
||||
JSCTOR_JSON_OBJECT = 1,
|
||||
JSCTOR_JSON_ARRAY = 2,
|
||||
JSCTOR_JSON_OBJECTAGG = 3,
|
||||
JSCTOR_JSON_ARRAYAGG = 4
|
||||
} JsonConstructorType;
|
||||
|
||||
/*
|
||||
* JsonConstructorExpr -
|
||||
* wrapper over FuncExpr/Aggref/WindowFunc for SQL/JSON constructors
|
||||
*/
|
||||
typedef struct JsonConstructorExpr
|
||||
{
|
||||
Expr xpr;
|
||||
JsonConstructorType type; /* constructor type */
|
||||
List *args;
|
||||
Expr *func; /* underlying json[b]_xxx() function call */
|
||||
Expr *coercion; /* coercion to RETURNING type */
|
||||
JsonReturning *returning; /* RETURNING clause */
|
||||
bool absent_on_null; /* ABSENT ON NULL? */
|
||||
bool unique; /* WITH UNIQUE KEYS? (JSON_OBJECT[AGG] only) */
|
||||
int location;
|
||||
} JsonConstructorExpr;
|
||||
|
||||
/* ----------------
|
||||
* NullTest
|
||||
*
|
||||
|
@ -26,6 +26,7 @@
|
||||
|
||||
/* name, value, category, is-bare-label */
|
||||
PG_KEYWORD("abort", ABORT_P, UNRESERVED_KEYWORD, BARE_LABEL)
|
||||
PG_KEYWORD("absent", ABSENT, UNRESERVED_KEYWORD, BARE_LABEL)
|
||||
PG_KEYWORD("absolute", ABSOLUTE_P, UNRESERVED_KEYWORD, BARE_LABEL)
|
||||
PG_KEYWORD("access", ACCESS, UNRESERVED_KEYWORD, BARE_LABEL)
|
||||
PG_KEYWORD("action", ACTION, UNRESERVED_KEYWORD, BARE_LABEL)
|
||||
@ -229,7 +230,12 @@ 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_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("key", KEY, UNRESERVED_KEYWORD, BARE_LABEL)
|
||||
PG_KEYWORD("keys", KEYS, UNRESERVED_KEYWORD, BARE_LABEL)
|
||||
PG_KEYWORD("label", LABEL, UNRESERVED_KEYWORD, BARE_LABEL)
|
||||
PG_KEYWORD("language", LANGUAGE, UNRESERVED_KEYWORD, BARE_LABEL)
|
||||
PG_KEYWORD("large", LARGE_P, UNRESERVED_KEYWORD, BARE_LABEL)
|
||||
|
@ -20,5 +20,11 @@
|
||||
extern void escape_json(StringInfo buf, const char *str);
|
||||
extern char *JsonEncodeDateTime(char *buf, Datum value, Oid typid,
|
||||
const int *tzp);
|
||||
extern bool to_json_is_immutable(Oid typoid);
|
||||
extern Datum json_build_object_worker(int nargs, Datum *args, bool *nulls,
|
||||
Oid *types, bool absent_on_null,
|
||||
bool unique_keys);
|
||||
extern Datum json_build_array_worker(int nargs, Datum *args, bool *nulls,
|
||||
Oid *types, bool absent_on_null);
|
||||
|
||||
#endif /* JSON_H */
|
||||
|
@ -329,6 +329,8 @@ typedef struct JsonbParseState
|
||||
JsonbValue contVal;
|
||||
Size size;
|
||||
struct JsonbParseState *next;
|
||||
bool unique_keys; /* Check object key uniqueness */
|
||||
bool skip_nulls; /* Skip null object fields */
|
||||
} JsonbParseState;
|
||||
|
||||
/*
|
||||
@ -412,4 +414,11 @@ extern Datum jsonb_set_element(Jsonb *jb, Datum *path, int path_len,
|
||||
JsonbValue *newval);
|
||||
extern Datum jsonb_get_element(Jsonb *jb, Datum *path, int npath,
|
||||
bool *isnull, bool as_text);
|
||||
extern bool to_jsonb_is_immutable(Oid typoid);
|
||||
extern Datum jsonb_build_object_worker(int nargs, Datum *args, bool *nulls,
|
||||
Oid *types, bool absent_on_null,
|
||||
bool unique_keys);
|
||||
extern Datum jsonb_build_array_worker(int nargs, Datum *args, bool *nulls,
|
||||
Oid *types, bool absent_on_null);
|
||||
|
||||
#endif /* __JSONB_H__ */
|
||||
|
@ -47,6 +47,8 @@ my %replace_string = (
|
||||
'NOT_LA' => 'not',
|
||||
'NULLS_LA' => 'nulls',
|
||||
'WITH_LA' => 'with',
|
||||
'WITH_LA_UNIQUE' => 'with',
|
||||
'WITHOUT_LA' => 'without',
|
||||
'TYPECAST' => '::',
|
||||
'DOT_DOT' => '..',
|
||||
'COLON_EQUALS' => ':=',
|
||||
|
@ -83,6 +83,7 @@ filtered_base_yylex(void)
|
||||
case WITH:
|
||||
case UIDENT:
|
||||
case USCONST:
|
||||
case WITHOUT:
|
||||
break;
|
||||
default:
|
||||
return cur_token;
|
||||
@ -143,6 +144,19 @@ filtered_base_yylex(void)
|
||||
case ORDINALITY:
|
||||
cur_token = WITH_LA;
|
||||
break;
|
||||
case UNIQUE:
|
||||
cur_token = WITH_LA_UNIQUE;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case WITHOUT:
|
||||
/* Replace WITHOUT by WITHOUT_LA if it's followed by TIME */
|
||||
switch (next_token)
|
||||
{
|
||||
case TIME:
|
||||
cur_token = WITHOUT_LA;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case UIDENT:
|
||||
|
@ -1473,8 +1473,10 @@ WHERE a.aggfnoid = p.oid AND
|
||||
NOT binary_coercible(p.proargtypes[1], ptr.proargtypes[2]))
|
||||
OR (p.pronargs > 2 AND
|
||||
NOT binary_coercible(p.proargtypes[2], ptr.proargtypes[3]))
|
||||
-- we could carry the check further, but 3 args is enough for now
|
||||
OR (p.pronargs > 3)
|
||||
OR (p.pronargs > 3 AND
|
||||
NOT binary_coercible(p.proargtypes[3], ptr.proargtypes[4]))
|
||||
-- we could carry the check further, but 4 args is enough for now
|
||||
OR (p.pronargs > 4)
|
||||
);
|
||||
aggfnoid | proname | oid | proname
|
||||
----------+---------+-----+---------
|
||||
|
746
src/test/regress/expected/sqljson.out
Normal file
746
src/test/regress/expected/sqljson.out
Normal file
@ -0,0 +1,746 @@
|
||||
-- JSON_OBJECT()
|
||||
SELECT JSON_OBJECT();
|
||||
json_object
|
||||
-------------
|
||||
{}
|
||||
(1 row)
|
||||
|
||||
SELECT JSON_OBJECT(RETURNING json);
|
||||
json_object
|
||||
-------------
|
||||
{}
|
||||
(1 row)
|
||||
|
||||
SELECT JSON_OBJECT(RETURNING json FORMAT JSON);
|
||||
json_object
|
||||
-------------
|
||||
{}
|
||||
(1 row)
|
||||
|
||||
SELECT JSON_OBJECT(RETURNING jsonb);
|
||||
json_object
|
||||
-------------
|
||||
{}
|
||||
(1 row)
|
||||
|
||||
SELECT JSON_OBJECT(RETURNING jsonb FORMAT JSON);
|
||||
json_object
|
||||
-------------
|
||||
{}
|
||||
(1 row)
|
||||
|
||||
SELECT JSON_OBJECT(RETURNING text);
|
||||
json_object
|
||||
-------------
|
||||
{}
|
||||
(1 row)
|
||||
|
||||
SELECT JSON_OBJECT(RETURNING text FORMAT JSON);
|
||||
json_object
|
||||
-------------
|
||||
{}
|
||||
(1 row)
|
||||
|
||||
SELECT JSON_OBJECT(RETURNING text FORMAT JSON ENCODING UTF8);
|
||||
ERROR: cannot set JSON encoding for non-bytea output types
|
||||
LINE 1: SELECT JSON_OBJECT(RETURNING text FORMAT JSON ENCODING UTF8)...
|
||||
^
|
||||
SELECT JSON_OBJECT(RETURNING text FORMAT JSON ENCODING INVALID_ENCODING);
|
||||
ERROR: unrecognized JSON encoding: invalid_encoding
|
||||
SELECT JSON_OBJECT(RETURNING bytea);
|
||||
json_object
|
||||
-------------
|
||||
\x7b7d
|
||||
(1 row)
|
||||
|
||||
SELECT JSON_OBJECT(RETURNING bytea FORMAT JSON);
|
||||
json_object
|
||||
-------------
|
||||
\x7b7d
|
||||
(1 row)
|
||||
|
||||
SELECT JSON_OBJECT(RETURNING bytea FORMAT JSON ENCODING UTF8);
|
||||
json_object
|
||||
-------------
|
||||
\x7b7d
|
||||
(1 row)
|
||||
|
||||
SELECT JSON_OBJECT(RETURNING bytea FORMAT JSON ENCODING UTF16);
|
||||
ERROR: unsupported JSON encoding
|
||||
LINE 1: SELECT JSON_OBJECT(RETURNING bytea FORMAT JSON ENCODING UTF1...
|
||||
^
|
||||
HINT: only UTF8 JSON encoding is supported
|
||||
SELECT JSON_OBJECT(RETURNING bytea FORMAT JSON ENCODING UTF32);
|
||||
ERROR: unsupported JSON encoding
|
||||
LINE 1: SELECT JSON_OBJECT(RETURNING bytea FORMAT JSON ENCODING UTF3...
|
||||
^
|
||||
HINT: only UTF8 JSON encoding is supported
|
||||
SELECT JSON_OBJECT('foo': NULL::int FORMAT JSON);
|
||||
ERROR: cannot use non-string types with explicit FORMAT JSON clause
|
||||
LINE 1: SELECT JSON_OBJECT('foo': NULL::int FORMAT JSON);
|
||||
^
|
||||
SELECT JSON_OBJECT('foo': NULL::int FORMAT JSON ENCODING UTF8);
|
||||
ERROR: JSON ENCODING clause is only allowed for bytea input type
|
||||
LINE 1: SELECT JSON_OBJECT('foo': NULL::int FORMAT JSON ENCODING UTF...
|
||||
^
|
||||
SELECT JSON_OBJECT('foo': NULL::json FORMAT JSON);
|
||||
WARNING: FORMAT JSON has no effect for json and jsonb types
|
||||
LINE 1: SELECT JSON_OBJECT('foo': NULL::json FORMAT JSON);
|
||||
^
|
||||
json_object
|
||||
----------------
|
||||
{"foo" : null}
|
||||
(1 row)
|
||||
|
||||
SELECT JSON_OBJECT('foo': NULL::json FORMAT JSON ENCODING UTF8);
|
||||
ERROR: JSON ENCODING clause is only allowed for bytea input type
|
||||
LINE 1: SELECT JSON_OBJECT('foo': NULL::json FORMAT JSON ENCODING UT...
|
||||
^
|
||||
SELECT JSON_OBJECT('foo': NULL::jsonb FORMAT JSON);
|
||||
WARNING: FORMAT JSON has no effect for json and jsonb types
|
||||
LINE 1: SELECT JSON_OBJECT('foo': NULL::jsonb FORMAT JSON);
|
||||
^
|
||||
json_object
|
||||
---------------
|
||||
{"foo": null}
|
||||
(1 row)
|
||||
|
||||
SELECT JSON_OBJECT('foo': NULL::jsonb FORMAT JSON ENCODING UTF8);
|
||||
ERROR: JSON ENCODING clause is only allowed for bytea input type
|
||||
LINE 1: SELECT JSON_OBJECT('foo': NULL::jsonb FORMAT JSON ENCODING U...
|
||||
^
|
||||
SELECT JSON_OBJECT(NULL: 1);
|
||||
ERROR: argument 1 cannot be null
|
||||
HINT: Object keys should be text.
|
||||
SELECT JSON_OBJECT('a': 2 + 3);
|
||||
json_object
|
||||
-------------
|
||||
{"a" : 5}
|
||||
(1 row)
|
||||
|
||||
SELECT JSON_OBJECT('a' VALUE 2 + 3);
|
||||
json_object
|
||||
-------------
|
||||
{"a" : 5}
|
||||
(1 row)
|
||||
|
||||
--SELECT JSON_OBJECT(KEY 'a' VALUE 2 + 3);
|
||||
SELECT JSON_OBJECT('a' || 2: 1);
|
||||
json_object
|
||||
-------------
|
||||
{"a2" : 1}
|
||||
(1 row)
|
||||
|
||||
SELECT JSON_OBJECT(('a' || 2) VALUE 1);
|
||||
json_object
|
||||
-------------
|
||||
{"a2" : 1}
|
||||
(1 row)
|
||||
|
||||
--SELECT JSON_OBJECT('a' || 2 VALUE 1);
|
||||
--SELECT JSON_OBJECT(KEY 'a' || 2 VALUE 1);
|
||||
SELECT JSON_OBJECT('a': 2::text);
|
||||
json_object
|
||||
-------------
|
||||
{"a" : "2"}
|
||||
(1 row)
|
||||
|
||||
SELECT JSON_OBJECT('a' VALUE 2::text);
|
||||
json_object
|
||||
-------------
|
||||
{"a" : "2"}
|
||||
(1 row)
|
||||
|
||||
--SELECT JSON_OBJECT(KEY 'a' VALUE 2::text);
|
||||
SELECT JSON_OBJECT(1::text: 2);
|
||||
json_object
|
||||
-------------
|
||||
{"1" : 2}
|
||||
(1 row)
|
||||
|
||||
SELECT JSON_OBJECT((1::text) VALUE 2);
|
||||
json_object
|
||||
-------------
|
||||
{"1" : 2}
|
||||
(1 row)
|
||||
|
||||
--SELECT JSON_OBJECT(1::text VALUE 2);
|
||||
--SELECT JSON_OBJECT(KEY 1::text VALUE 2);
|
||||
SELECT JSON_OBJECT(json '[1]': 123);
|
||||
ERROR: key value must be scalar, not array, composite, or json
|
||||
SELECT JSON_OBJECT(ARRAY[1,2,3]: 'aaa');
|
||||
ERROR: key value must be scalar, not array, composite, or json
|
||||
SELECT JSON_OBJECT(
|
||||
'a': '123',
|
||||
1.23: 123,
|
||||
'c': json '[ 1,true,{ } ]',
|
||||
'd': jsonb '{ "x" : 123.45 }'
|
||||
);
|
||||
json_object
|
||||
-------------------------------------------------------------------
|
||||
{"a": "123", "c": [1, true, {}], "d": {"x": 123.45}, "1.23": 123}
|
||||
(1 row)
|
||||
|
||||
SELECT JSON_OBJECT(
|
||||
'a': '123',
|
||||
1.23: 123,
|
||||
'c': json '[ 1,true,{ } ]',
|
||||
'd': jsonb '{ "x" : 123.45 }'
|
||||
RETURNING jsonb
|
||||
);
|
||||
json_object
|
||||
-------------------------------------------------------------------
|
||||
{"a": "123", "c": [1, true, {}], "d": {"x": 123.45}, "1.23": 123}
|
||||
(1 row)
|
||||
|
||||
/*
|
||||
SELECT JSON_OBJECT(
|
||||
'a': '123',
|
||||
KEY 1.23 VALUE 123,
|
||||
'c' VALUE json '[1, true, {}]'
|
||||
);
|
||||
*/
|
||||
SELECT JSON_OBJECT('a': '123', 'b': JSON_OBJECT('a': 111, 'b': 'aaa'));
|
||||
json_object
|
||||
-----------------------------------------------
|
||||
{"a" : "123", "b" : {"a" : 111, "b" : "aaa"}}
|
||||
(1 row)
|
||||
|
||||
SELECT JSON_OBJECT('a': '123', 'b': JSON_OBJECT('a': 111, 'b': 'aaa' RETURNING jsonb));
|
||||
json_object
|
||||
-------------------------------------------
|
||||
{"a": "123", "b": {"a": 111, "b": "aaa"}}
|
||||
(1 row)
|
||||
|
||||
SELECT JSON_OBJECT('a': JSON_OBJECT('b': 1 RETURNING text));
|
||||
json_object
|
||||
-----------------------
|
||||
{"a" : "{\"b\" : 1}"}
|
||||
(1 row)
|
||||
|
||||
SELECT JSON_OBJECT('a': JSON_OBJECT('b': 1 RETURNING text) FORMAT JSON);
|
||||
json_object
|
||||
-------------------
|
||||
{"a" : {"b" : 1}}
|
||||
(1 row)
|
||||
|
||||
SELECT JSON_OBJECT('a': JSON_OBJECT('b': 1 RETURNING bytea));
|
||||
json_object
|
||||
---------------------------------
|
||||
{"a" : "\\x7b226222203a20317d"}
|
||||
(1 row)
|
||||
|
||||
SELECT JSON_OBJECT('a': JSON_OBJECT('b': 1 RETURNING bytea) FORMAT JSON);
|
||||
json_object
|
||||
-------------------
|
||||
{"a" : {"b" : 1}}
|
||||
(1 row)
|
||||
|
||||
SELECT JSON_OBJECT('a': '1', 'b': NULL, 'c': 2);
|
||||
json_object
|
||||
----------------------------------
|
||||
{"a" : "1", "b" : null, "c" : 2}
|
||||
(1 row)
|
||||
|
||||
SELECT JSON_OBJECT('a': '1', 'b': NULL, 'c': 2 NULL ON NULL);
|
||||
json_object
|
||||
----------------------------------
|
||||
{"a" : "1", "b" : null, "c" : 2}
|
||||
(1 row)
|
||||
|
||||
SELECT JSON_OBJECT('a': '1', 'b': NULL, 'c': 2 ABSENT ON NULL);
|
||||
json_object
|
||||
----------------------
|
||||
{"a" : "1", "c" : 2}
|
||||
(1 row)
|
||||
|
||||
SELECT JSON_OBJECT(1: 1, '1': NULL WITH UNIQUE);
|
||||
ERROR: duplicate JSON key "1"
|
||||
SELECT JSON_OBJECT(1: 1, '1': NULL ABSENT ON NULL WITH UNIQUE);
|
||||
ERROR: duplicate JSON key "1"
|
||||
SELECT JSON_OBJECT(1: 1, '1': NULL NULL ON NULL WITH UNIQUE RETURNING jsonb);
|
||||
ERROR: duplicate JSON object key value
|
||||
SELECT JSON_OBJECT(1: 1, '1': NULL ABSENT ON NULL WITH UNIQUE RETURNING jsonb);
|
||||
ERROR: duplicate JSON object key value
|
||||
SELECT JSON_OBJECT(1: 1, '2': NULL, '1': 1 NULL ON NULL WITH UNIQUE);
|
||||
ERROR: duplicate JSON key "1"
|
||||
SELECT JSON_OBJECT(1: 1, '2': NULL, '1': 1 ABSENT ON NULL WITH UNIQUE);
|
||||
ERROR: duplicate JSON key "1"
|
||||
SELECT JSON_OBJECT(1: 1, '2': NULL, '1': 1 ABSENT ON NULL WITHOUT UNIQUE);
|
||||
json_object
|
||||
--------------------
|
||||
{"1" : 1, "1" : 1}
|
||||
(1 row)
|
||||
|
||||
SELECT JSON_OBJECT(1: 1, '2': NULL, '1': 1 ABSENT ON NULL WITH UNIQUE RETURNING jsonb);
|
||||
ERROR: duplicate JSON object key value
|
||||
SELECT JSON_OBJECT(1: 1, '2': NULL, '1': 1 ABSENT ON NULL WITHOUT UNIQUE RETURNING jsonb);
|
||||
json_object
|
||||
-------------
|
||||
{"1": 1}
|
||||
(1 row)
|
||||
|
||||
SELECT JSON_OBJECT(1: 1, '2': NULL, '3': 1, 4: NULL, '5': 'a' ABSENT ON NULL WITH UNIQUE RETURNING jsonb);
|
||||
json_object
|
||||
----------------------------
|
||||
{"1": 1, "3": 1, "5": "a"}
|
||||
(1 row)
|
||||
|
||||
-- JSON_ARRAY()
|
||||
SELECT JSON_ARRAY();
|
||||
json_array
|
||||
------------
|
||||
[]
|
||||
(1 row)
|
||||
|
||||
SELECT JSON_ARRAY(RETURNING json);
|
||||
json_array
|
||||
------------
|
||||
[]
|
||||
(1 row)
|
||||
|
||||
SELECT JSON_ARRAY(RETURNING json FORMAT JSON);
|
||||
json_array
|
||||
------------
|
||||
[]
|
||||
(1 row)
|
||||
|
||||
SELECT JSON_ARRAY(RETURNING jsonb);
|
||||
json_array
|
||||
------------
|
||||
[]
|
||||
(1 row)
|
||||
|
||||
SELECT JSON_ARRAY(RETURNING jsonb FORMAT JSON);
|
||||
json_array
|
||||
------------
|
||||
[]
|
||||
(1 row)
|
||||
|
||||
SELECT JSON_ARRAY(RETURNING text);
|
||||
json_array
|
||||
------------
|
||||
[]
|
||||
(1 row)
|
||||
|
||||
SELECT JSON_ARRAY(RETURNING text FORMAT JSON);
|
||||
json_array
|
||||
------------
|
||||
[]
|
||||
(1 row)
|
||||
|
||||
SELECT JSON_ARRAY(RETURNING text FORMAT JSON ENCODING UTF8);
|
||||
ERROR: cannot set JSON encoding for non-bytea output types
|
||||
LINE 1: SELECT JSON_ARRAY(RETURNING text FORMAT JSON ENCODING UTF8);
|
||||
^
|
||||
SELECT JSON_ARRAY(RETURNING text FORMAT JSON ENCODING INVALID_ENCODING);
|
||||
ERROR: unrecognized JSON encoding: invalid_encoding
|
||||
SELECT JSON_ARRAY(RETURNING bytea);
|
||||
json_array
|
||||
------------
|
||||
\x5b5d
|
||||
(1 row)
|
||||
|
||||
SELECT JSON_ARRAY(RETURNING bytea FORMAT JSON);
|
||||
json_array
|
||||
------------
|
||||
\x5b5d
|
||||
(1 row)
|
||||
|
||||
SELECT JSON_ARRAY(RETURNING bytea FORMAT JSON ENCODING UTF8);
|
||||
json_array
|
||||
------------
|
||||
\x5b5d
|
||||
(1 row)
|
||||
|
||||
SELECT JSON_ARRAY(RETURNING bytea FORMAT JSON ENCODING UTF16);
|
||||
ERROR: unsupported JSON encoding
|
||||
LINE 1: SELECT JSON_ARRAY(RETURNING bytea FORMAT JSON ENCODING UTF16...
|
||||
^
|
||||
HINT: only UTF8 JSON encoding is supported
|
||||
SELECT JSON_ARRAY(RETURNING bytea FORMAT JSON ENCODING UTF32);
|
||||
ERROR: unsupported JSON encoding
|
||||
LINE 1: SELECT JSON_ARRAY(RETURNING bytea FORMAT JSON ENCODING UTF32...
|
||||
^
|
||||
HINT: only UTF8 JSON encoding is supported
|
||||
SELECT JSON_ARRAY('aaa', 111, true, array[1,2,3], NULL, json '{"a": [1]}', jsonb '["a",3]');
|
||||
json_array
|
||||
-----------------------------------------------------
|
||||
["aaa", 111, true, [1, 2, 3], {"a": [1]}, ["a", 3]]
|
||||
(1 row)
|
||||
|
||||
SELECT JSON_ARRAY('a', NULL, 'b' NULL ON NULL);
|
||||
json_array
|
||||
------------------
|
||||
["a", null, "b"]
|
||||
(1 row)
|
||||
|
||||
SELECT JSON_ARRAY('a', NULL, 'b' ABSENT ON NULL);
|
||||
json_array
|
||||
------------
|
||||
["a", "b"]
|
||||
(1 row)
|
||||
|
||||
SELECT JSON_ARRAY(NULL, NULL, 'b' ABSENT ON NULL);
|
||||
json_array
|
||||
------------
|
||||
["b"]
|
||||
(1 row)
|
||||
|
||||
SELECT JSON_ARRAY('a', NULL, 'b' NULL ON NULL RETURNING jsonb);
|
||||
json_array
|
||||
------------------
|
||||
["a", null, "b"]
|
||||
(1 row)
|
||||
|
||||
SELECT JSON_ARRAY('a', NULL, 'b' ABSENT ON NULL RETURNING jsonb);
|
||||
json_array
|
||||
------------
|
||||
["a", "b"]
|
||||
(1 row)
|
||||
|
||||
SELECT JSON_ARRAY(NULL, NULL, 'b' ABSENT ON NULL RETURNING jsonb);
|
||||
json_array
|
||||
------------
|
||||
["b"]
|
||||
(1 row)
|
||||
|
||||
SELECT JSON_ARRAY(JSON_ARRAY('{ "a" : 123 }' RETURNING text));
|
||||
json_array
|
||||
-------------------------------
|
||||
["[\"{ \\\"a\\\" : 123 }\"]"]
|
||||
(1 row)
|
||||
|
||||
SELECT JSON_ARRAY(JSON_ARRAY('{ "a" : 123 }' FORMAT JSON RETURNING text));
|
||||
json_array
|
||||
-----------------------
|
||||
["[{ \"a\" : 123 }]"]
|
||||
(1 row)
|
||||
|
||||
SELECT JSON_ARRAY(JSON_ARRAY('{ "a" : 123 }' FORMAT JSON RETURNING text) FORMAT JSON);
|
||||
json_array
|
||||
-------------------
|
||||
[[{ "a" : 123 }]]
|
||||
(1 row)
|
||||
|
||||
SELECT JSON_ARRAY(SELECT i FROM (VALUES (1), (2), (NULL), (4)) foo(i));
|
||||
json_array
|
||||
------------
|
||||
[1, 2, 4]
|
||||
(1 row)
|
||||
|
||||
SELECT JSON_ARRAY(SELECT i FROM (VALUES (NULL::int[]), ('{1,2}'), (NULL), (NULL), ('{3,4}'), (NULL)) foo(i));
|
||||
json_array
|
||||
------------
|
||||
[[1,2], +
|
||||
[3,4]]
|
||||
(1 row)
|
||||
|
||||
SELECT JSON_ARRAY(SELECT i FROM (VALUES (NULL::int[]), ('{1,2}'), (NULL), (NULL), ('{3,4}'), (NULL)) foo(i) RETURNING jsonb);
|
||||
json_array
|
||||
------------------
|
||||
[[1, 2], [3, 4]]
|
||||
(1 row)
|
||||
|
||||
--SELECT JSON_ARRAY(SELECT i FROM (VALUES (NULL::int[]), ('{1,2}'), (NULL), (NULL), ('{3,4}'), (NULL)) foo(i) NULL ON NULL);
|
||||
--SELECT JSON_ARRAY(SELECT i FROM (VALUES (NULL::int[]), ('{1,2}'), (NULL), (NULL), ('{3,4}'), (NULL)) foo(i) NULL ON NULL RETURNING jsonb);
|
||||
SELECT JSON_ARRAY(SELECT i FROM (VALUES (3), (1), (NULL), (2)) foo(i) ORDER BY i);
|
||||
json_array
|
||||
------------
|
||||
[1, 2, 3]
|
||||
(1 row)
|
||||
|
||||
-- Should fail
|
||||
SELECT JSON_ARRAY(SELECT FROM (VALUES (1)) foo(i));
|
||||
ERROR: subquery must return only one column
|
||||
LINE 1: SELECT JSON_ARRAY(SELECT FROM (VALUES (1)) foo(i));
|
||||
^
|
||||
SELECT JSON_ARRAY(SELECT i, i FROM (VALUES (1)) foo(i));
|
||||
ERROR: subquery must return only one column
|
||||
LINE 1: SELECT JSON_ARRAY(SELECT i, i FROM (VALUES (1)) foo(i));
|
||||
^
|
||||
SELECT JSON_ARRAY(SELECT * FROM (VALUES (1, 2)) foo(i, j));
|
||||
ERROR: subquery must return only one column
|
||||
LINE 1: SELECT JSON_ARRAY(SELECT * FROM (VALUES (1, 2)) foo(i, j));
|
||||
^
|
||||
-- JSON_ARRAYAGG()
|
||||
SELECT JSON_ARRAYAGG(i) IS NULL,
|
||||
JSON_ARRAYAGG(i RETURNING jsonb) IS NULL
|
||||
FROM generate_series(1, 0) i;
|
||||
?column? | ?column?
|
||||
----------+----------
|
||||
t | t
|
||||
(1 row)
|
||||
|
||||
SELECT JSON_ARRAYAGG(i),
|
||||
JSON_ARRAYAGG(i RETURNING jsonb)
|
||||
FROM generate_series(1, 5) i;
|
||||
json_arrayagg | json_arrayagg
|
||||
-----------------+-----------------
|
||||
[1, 2, 3, 4, 5] | [1, 2, 3, 4, 5]
|
||||
(1 row)
|
||||
|
||||
SELECT JSON_ARRAYAGG(i ORDER BY i DESC)
|
||||
FROM generate_series(1, 5) i;
|
||||
json_arrayagg
|
||||
-----------------
|
||||
[5, 4, 3, 2, 1]
|
||||
(1 row)
|
||||
|
||||
SELECT JSON_ARRAYAGG(i::text::json)
|
||||
FROM generate_series(1, 5) i;
|
||||
json_arrayagg
|
||||
-----------------
|
||||
[1, 2, 3, 4, 5]
|
||||
(1 row)
|
||||
|
||||
SELECT JSON_ARRAYAGG(JSON_ARRAY(i, i + 1 RETURNING text) FORMAT JSON)
|
||||
FROM generate_series(1, 5) i;
|
||||
json_arrayagg
|
||||
------------------------------------------
|
||||
[[1, 2], [2, 3], [3, 4], [4, 5], [5, 6]]
|
||||
(1 row)
|
||||
|
||||
SELECT JSON_ARRAYAGG(NULL),
|
||||
JSON_ARRAYAGG(NULL RETURNING jsonb)
|
||||
FROM generate_series(1, 5);
|
||||
json_arrayagg | json_arrayagg
|
||||
---------------+---------------
|
||||
[] | []
|
||||
(1 row)
|
||||
|
||||
SELECT JSON_ARRAYAGG(NULL NULL ON NULL),
|
||||
JSON_ARRAYAGG(NULL NULL ON NULL RETURNING jsonb)
|
||||
FROM generate_series(1, 5);
|
||||
json_arrayagg | json_arrayagg
|
||||
--------------------------------+--------------------------------
|
||||
[null, null, null, null, null] | [null, null, null, null, null]
|
||||
(1 row)
|
||||
|
||||
SELECT
|
||||
JSON_ARRAYAGG(bar),
|
||||
JSON_ARRAYAGG(bar RETURNING jsonb),
|
||||
JSON_ARRAYAGG(bar ABSENT ON NULL),
|
||||
JSON_ARRAYAGG(bar ABSENT ON NULL RETURNING jsonb),
|
||||
JSON_ARRAYAGG(bar NULL ON NULL),
|
||||
JSON_ARRAYAGG(bar NULL ON NULL RETURNING jsonb),
|
||||
JSON_ARRAYAGG(foo),
|
||||
JSON_ARRAYAGG(foo RETURNING jsonb),
|
||||
JSON_ARRAYAGG(foo ORDER BY bar) FILTER (WHERE bar > 2),
|
||||
JSON_ARRAYAGG(foo ORDER BY bar RETURNING jsonb) FILTER (WHERE bar > 2)
|
||||
FROM
|
||||
(VALUES (NULL), (3), (1), (NULL), (NULL), (5), (2), (4), (NULL)) foo(bar);
|
||||
json_arrayagg | json_arrayagg | json_arrayagg | json_arrayagg | json_arrayagg | json_arrayagg | json_arrayagg | json_arrayagg | json_arrayagg | json_arrayagg
|
||||
-----------------+-----------------+-----------------+-----------------+-----------------------------------------+-----------------------------------------+-----------------+--------------------------------------------------------------------------------------------------------------------------+---------------+--------------------------------------
|
||||
[3, 1, 5, 2, 4] | [3, 1, 5, 2, 4] | [3, 1, 5, 2, 4] | [3, 1, 5, 2, 4] | [null, 3, 1, null, null, 5, 2, 4, null] | [null, 3, 1, null, null, 5, 2, 4, null] | [{"bar":null}, +| [{"bar": null}, {"bar": 3}, {"bar": 1}, {"bar": null}, {"bar": null}, {"bar": 5}, {"bar": 2}, {"bar": 4}, {"bar": null}] | [{"bar":3}, +| [{"bar": 3}, {"bar": 4}, {"bar": 5}]
|
||||
| | | | | | {"bar":3}, +| | {"bar":4}, +|
|
||||
| | | | | | {"bar":1}, +| | {"bar":5}] |
|
||||
| | | | | | {"bar":null}, +| | |
|
||||
| | | | | | {"bar":null}, +| | |
|
||||
| | | | | | {"bar":5}, +| | |
|
||||
| | | | | | {"bar":2}, +| | |
|
||||
| | | | | | {"bar":4}, +| | |
|
||||
| | | | | | {"bar":null}] | | |
|
||||
(1 row)
|
||||
|
||||
SELECT
|
||||
bar, JSON_ARRAYAGG(bar) FILTER (WHERE bar > 2) OVER (PARTITION BY foo.bar % 2)
|
||||
FROM
|
||||
(VALUES (NULL), (3), (1), (NULL), (NULL), (5), (2), (4), (NULL), (5), (4)) foo(bar);
|
||||
bar | json_arrayagg
|
||||
-----+---------------
|
||||
4 | [4, 4]
|
||||
4 | [4, 4]
|
||||
2 | [4, 4]
|
||||
5 | [5, 3, 5]
|
||||
3 | [5, 3, 5]
|
||||
1 | [5, 3, 5]
|
||||
5 | [5, 3, 5]
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
(11 rows)
|
||||
|
||||
-- JSON_OBJECTAGG()
|
||||
SELECT JSON_OBJECTAGG('key': 1) IS NULL,
|
||||
JSON_OBJECTAGG('key': 1 RETURNING jsonb) IS NULL
|
||||
WHERE FALSE;
|
||||
?column? | ?column?
|
||||
----------+----------
|
||||
t | t
|
||||
(1 row)
|
||||
|
||||
SELECT JSON_OBJECTAGG(NULL: 1);
|
||||
ERROR: field name must not be null
|
||||
SELECT JSON_OBJECTAGG(NULL: 1 RETURNING jsonb);
|
||||
ERROR: field name must not be null
|
||||
SELECT
|
||||
JSON_OBJECTAGG(i: i),
|
||||
-- JSON_OBJECTAGG(i VALUE i),
|
||||
-- JSON_OBJECTAGG(KEY i VALUE i),
|
||||
JSON_OBJECTAGG(i: i RETURNING jsonb)
|
||||
FROM
|
||||
generate_series(1, 5) i;
|
||||
json_objectagg | json_objectagg
|
||||
-------------------------------------------------+------------------------------------------
|
||||
{ "1" : 1, "2" : 2, "3" : 3, "4" : 4, "5" : 5 } | {"1": 1, "2": 2, "3": 3, "4": 4, "5": 5}
|
||||
(1 row)
|
||||
|
||||
SELECT
|
||||
JSON_OBJECTAGG(k: v),
|
||||
JSON_OBJECTAGG(k: v NULL ON NULL),
|
||||
JSON_OBJECTAGG(k: v ABSENT ON NULL),
|
||||
JSON_OBJECTAGG(k: v RETURNING jsonb),
|
||||
JSON_OBJECTAGG(k: v NULL ON NULL RETURNING jsonb),
|
||||
JSON_OBJECTAGG(k: v ABSENT ON NULL RETURNING jsonb)
|
||||
FROM
|
||||
(VALUES (1, 1), (1, NULL), (2, NULL), (3, 3)) foo(k, v);
|
||||
json_objectagg | json_objectagg | json_objectagg | json_objectagg | json_objectagg | json_objectagg
|
||||
----------------------------------------------+----------------------------------------------+----------------------+--------------------------------+--------------------------------+------------------
|
||||
{ "1" : 1, "1" : null, "2" : null, "3" : 3 } | { "1" : 1, "1" : null, "2" : null, "3" : 3 } | { "1" : 1, "3" : 3 } | {"1": null, "2": null, "3": 3} | {"1": null, "2": null, "3": 3} | {"1": 1, "3": 3}
|
||||
(1 row)
|
||||
|
||||
SELECT JSON_OBJECTAGG(k: v WITH UNIQUE KEYS)
|
||||
FROM (VALUES (1, 1), (1, NULL), (2, 2)) foo(k, v);
|
||||
ERROR: duplicate JSON key "1"
|
||||
SELECT JSON_OBJECTAGG(k: v ABSENT ON NULL WITH UNIQUE KEYS)
|
||||
FROM (VALUES (1, 1), (1, NULL), (2, 2)) foo(k, v);
|
||||
ERROR: duplicate JSON key "1"
|
||||
SELECT JSON_OBJECTAGG(k: v ABSENT ON NULL WITH UNIQUE KEYS)
|
||||
FROM (VALUES (1, 1), (0, NULL), (3, NULL), (2, 2), (4, NULL)) foo(k, v);
|
||||
json_objectagg
|
||||
----------------------
|
||||
{ "1" : 1, "2" : 2 }
|
||||
(1 row)
|
||||
|
||||
SELECT JSON_OBJECTAGG(k: v WITH UNIQUE KEYS RETURNING jsonb)
|
||||
FROM (VALUES (1, 1), (1, NULL), (2, 2)) foo(k, v);
|
||||
ERROR: duplicate JSON object key value
|
||||
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 value
|
||||
-- Test JSON_OBJECT deparsing
|
||||
EXPLAIN (VERBOSE, COSTS OFF)
|
||||
SELECT JSON_OBJECT('foo' : '1' FORMAT JSON, 'bar' : 'baz' RETURNING json);
|
||||
QUERY PLAN
|
||||
------------------------------------------------------------------------------
|
||||
Result
|
||||
Output: JSON_OBJECT('foo' : '1'::json, 'bar' : 'baz'::text RETURNING json)
|
||||
(2 rows)
|
||||
|
||||
CREATE VIEW json_object_view AS
|
||||
SELECT JSON_OBJECT('foo' : '1' FORMAT JSON, 'bar' : 'baz' RETURNING json);
|
||||
\sv json_object_view
|
||||
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;
|
||||
-- Test JSON_ARRAY deparsing
|
||||
EXPLAIN (VERBOSE, COSTS OFF)
|
||||
SELECT JSON_ARRAY('1' FORMAT JSON, 2 RETURNING json);
|
||||
QUERY PLAN
|
||||
---------------------------------------------------
|
||||
Result
|
||||
Output: JSON_ARRAY('1'::json, 2 RETURNING json)
|
||||
(2 rows)
|
||||
|
||||
CREATE VIEW json_array_view AS
|
||||
SELECT JSON_ARRAY('1' FORMAT JSON, 2 RETURNING json);
|
||||
\sv json_array_view
|
||||
CREATE OR REPLACE VIEW public.json_array_view AS
|
||||
SELECT JSON_ARRAY('1'::text FORMAT JSON, 2 RETURNING json) AS "json_array"
|
||||
DROP VIEW json_array_view;
|
||||
-- Test JSON_OBJECTAGG deparsing
|
||||
EXPLAIN (VERBOSE, COSTS OFF)
|
||||
SELECT JSON_OBJECTAGG(i: ('111' || i)::bytea FORMAT JSON WITH UNIQUE RETURNING text) FILTER (WHERE i > 3)
|
||||
FROM generate_series(1,5) i;
|
||||
QUERY PLAN
|
||||
--------------------------------------------------------------------------------------------------------------------------------------
|
||||
Aggregate
|
||||
Output: JSON_OBJECTAGG(i : (('111'::text || (i)::text))::bytea FORMAT JSON WITH UNIQUE KEYS RETURNING text) FILTER (WHERE (i > 3))
|
||||
-> Function Scan on pg_catalog.generate_series i
|
||||
Output: i
|
||||
Function Call: generate_series(1, 5)
|
||||
(5 rows)
|
||||
|
||||
EXPLAIN (VERBOSE, COSTS OFF)
|
||||
SELECT JSON_OBJECTAGG(i: ('111' || i)::bytea FORMAT JSON WITH UNIQUE RETURNING text) OVER (PARTITION BY i % 2)
|
||||
FROM generate_series(1,5) i;
|
||||
QUERY PLAN
|
||||
-----------------------------------------------------------------------------------------------------------------------------------
|
||||
WindowAgg
|
||||
Output: JSON_OBJECTAGG(i : (('111'::text || (i)::text))::bytea FORMAT JSON WITH UNIQUE KEYS RETURNING text) OVER (?), ((i % 2))
|
||||
-> Sort
|
||||
Output: ((i % 2)), i
|
||||
Sort Key: ((i.i % 2))
|
||||
-> Function Scan on pg_catalog.generate_series i
|
||||
Output: (i % 2), i
|
||||
Function Call: generate_series(1, 5)
|
||||
(8 rows)
|
||||
|
||||
CREATE VIEW json_objectagg_view AS
|
||||
SELECT JSON_OBJECTAGG(i: ('111' || i)::bytea FORMAT JSON WITH UNIQUE RETURNING text) FILTER (WHERE i > 3)
|
||||
FROM generate_series(1,5) i;
|
||||
\sv json_objectagg_view
|
||||
CREATE OR REPLACE VIEW public.json_objectagg_view AS
|
||||
SELECT JSON_OBJECTAGG(i.i : ('111'::text || i.i)::bytea FORMAT JSON WITH UNIQUE KEYS RETURNING text) FILTER (WHERE i.i > 3) AS "json_objectagg"
|
||||
FROM generate_series(1, 5) i(i)
|
||||
DROP VIEW json_objectagg_view;
|
||||
-- Test JSON_ARRAYAGG deparsing
|
||||
EXPLAIN (VERBOSE, COSTS OFF)
|
||||
SELECT JSON_ARRAYAGG(('111' || i)::bytea FORMAT JSON NULL ON NULL RETURNING text) FILTER (WHERE i > 3)
|
||||
FROM generate_series(1,5) i;
|
||||
QUERY PLAN
|
||||
-----------------------------------------------------------------------------------------------------------------------------
|
||||
Aggregate
|
||||
Output: JSON_ARRAYAGG((('111'::text || (i)::text))::bytea FORMAT JSON NULL ON NULL RETURNING text) FILTER (WHERE (i > 3))
|
||||
-> Function Scan on pg_catalog.generate_series i
|
||||
Output: i
|
||||
Function Call: generate_series(1, 5)
|
||||
(5 rows)
|
||||
|
||||
EXPLAIN (VERBOSE, COSTS OFF)
|
||||
SELECT JSON_ARRAYAGG(('111' || i)::bytea FORMAT JSON NULL ON NULL RETURNING text) OVER (PARTITION BY i % 2)
|
||||
FROM generate_series(1,5) i;
|
||||
QUERY PLAN
|
||||
--------------------------------------------------------------------------------------------------------------------------
|
||||
WindowAgg
|
||||
Output: JSON_ARRAYAGG((('111'::text || (i)::text))::bytea FORMAT JSON NULL ON NULL RETURNING text) OVER (?), ((i % 2))
|
||||
-> Sort
|
||||
Output: ((i % 2)), i
|
||||
Sort Key: ((i.i % 2))
|
||||
-> Function Scan on pg_catalog.generate_series i
|
||||
Output: (i % 2), i
|
||||
Function Call: generate_series(1, 5)
|
||||
(8 rows)
|
||||
|
||||
CREATE VIEW json_arrayagg_view AS
|
||||
SELECT JSON_ARRAYAGG(('111' || i)::bytea FORMAT JSON NULL ON NULL RETURNING text) FILTER (WHERE i > 3)
|
||||
FROM generate_series(1,5) i;
|
||||
\sv json_arrayagg_view
|
||||
CREATE OR REPLACE VIEW public.json_arrayagg_view AS
|
||||
SELECT JSON_ARRAYAGG(('111'::text || i.i)::bytea FORMAT JSON NULL ON NULL RETURNING text) FILTER (WHERE i.i > 3) AS "json_arrayagg"
|
||||
FROM generate_series(1, 5) i(i)
|
||||
DROP VIEW json_arrayagg_view;
|
||||
-- Test JSON_ARRAY(subquery) deparsing
|
||||
EXPLAIN (VERBOSE, COSTS OFF)
|
||||
SELECT JSON_ARRAY(SELECT i FROM (VALUES (1), (2), (NULL), (4)) foo(i) RETURNING jsonb);
|
||||
QUERY PLAN
|
||||
---------------------------------------------------------------------
|
||||
Result
|
||||
Output: $0
|
||||
InitPlan 1 (returns $0)
|
||||
-> Aggregate
|
||||
Output: JSON_ARRAYAGG("*VALUES*".column1 RETURNING jsonb)
|
||||
-> Values Scan on "*VALUES*"
|
||||
Output: "*VALUES*".column1
|
||||
(7 rows)
|
||||
|
||||
CREATE VIEW json_array_subquery_view AS
|
||||
SELECT JSON_ARRAY(SELECT i FROM (VALUES (1), (2), (NULL), (4)) foo(i) RETURNING jsonb);
|
||||
\sv json_array_subquery_view
|
||||
CREATE OR REPLACE VIEW public.json_array_subquery_view AS
|
||||
SELECT ( SELECT JSON_ARRAYAGG(q.a RETURNING jsonb) AS "json_arrayagg"
|
||||
FROM ( SELECT foo.i
|
||||
FROM ( VALUES (1), (2), (NULL::integer), (4)) foo(i)) q(a)) AS "json_array"
|
||||
DROP VIEW json_array_subquery_view;
|
@ -111,7 +111,7 @@ test: select_views portals_p2 foreign_key cluster dependency guc bitmapops combo
|
||||
# ----------
|
||||
# Another group of parallel tests (JSON related)
|
||||
# ----------
|
||||
test: json jsonb json_encoding jsonpath jsonpath_encoding jsonb_jsonpath
|
||||
test: json jsonb json_encoding jsonpath jsonpath_encoding jsonb_jsonpath sqljson
|
||||
|
||||
# ----------
|
||||
# Another group of parallel tests
|
||||
|
@ -854,8 +854,10 @@ WHERE a.aggfnoid = p.oid AND
|
||||
NOT binary_coercible(p.proargtypes[1], ptr.proargtypes[2]))
|
||||
OR (p.pronargs > 2 AND
|
||||
NOT binary_coercible(p.proargtypes[2], ptr.proargtypes[3]))
|
||||
-- we could carry the check further, but 3 args is enough for now
|
||||
OR (p.pronargs > 3)
|
||||
OR (p.pronargs > 3 AND
|
||||
NOT binary_coercible(p.proargtypes[3], ptr.proargtypes[4]))
|
||||
-- we could carry the check further, but 4 args is enough for now
|
||||
OR (p.pronargs > 4)
|
||||
);
|
||||
|
||||
-- Cross-check finalfn (if present) against its entry in pg_proc.
|
||||
|
282
src/test/regress/sql/sqljson.sql
Normal file
282
src/test/regress/sql/sqljson.sql
Normal file
@ -0,0 +1,282 @@
|
||||
-- JSON_OBJECT()
|
||||
SELECT JSON_OBJECT();
|
||||
SELECT JSON_OBJECT(RETURNING json);
|
||||
SELECT JSON_OBJECT(RETURNING json FORMAT JSON);
|
||||
SELECT JSON_OBJECT(RETURNING jsonb);
|
||||
SELECT JSON_OBJECT(RETURNING jsonb FORMAT JSON);
|
||||
SELECT JSON_OBJECT(RETURNING text);
|
||||
SELECT JSON_OBJECT(RETURNING text FORMAT JSON);
|
||||
SELECT JSON_OBJECT(RETURNING text FORMAT JSON ENCODING UTF8);
|
||||
SELECT JSON_OBJECT(RETURNING text FORMAT JSON ENCODING INVALID_ENCODING);
|
||||
SELECT JSON_OBJECT(RETURNING bytea);
|
||||
SELECT JSON_OBJECT(RETURNING bytea FORMAT JSON);
|
||||
SELECT JSON_OBJECT(RETURNING bytea FORMAT JSON ENCODING UTF8);
|
||||
SELECT JSON_OBJECT(RETURNING bytea FORMAT JSON ENCODING UTF16);
|
||||
SELECT JSON_OBJECT(RETURNING bytea FORMAT JSON ENCODING UTF32);
|
||||
|
||||
SELECT JSON_OBJECT('foo': NULL::int FORMAT JSON);
|
||||
SELECT JSON_OBJECT('foo': NULL::int FORMAT JSON ENCODING UTF8);
|
||||
SELECT JSON_OBJECT('foo': NULL::json FORMAT JSON);
|
||||
SELECT JSON_OBJECT('foo': NULL::json FORMAT JSON ENCODING UTF8);
|
||||
SELECT JSON_OBJECT('foo': NULL::jsonb FORMAT JSON);
|
||||
SELECT JSON_OBJECT('foo': NULL::jsonb FORMAT JSON ENCODING UTF8);
|
||||
|
||||
SELECT JSON_OBJECT(NULL: 1);
|
||||
SELECT JSON_OBJECT('a': 2 + 3);
|
||||
SELECT JSON_OBJECT('a' VALUE 2 + 3);
|
||||
--SELECT JSON_OBJECT(KEY 'a' VALUE 2 + 3);
|
||||
SELECT JSON_OBJECT('a' || 2: 1);
|
||||
SELECT JSON_OBJECT(('a' || 2) VALUE 1);
|
||||
--SELECT JSON_OBJECT('a' || 2 VALUE 1);
|
||||
--SELECT JSON_OBJECT(KEY 'a' || 2 VALUE 1);
|
||||
SELECT JSON_OBJECT('a': 2::text);
|
||||
SELECT JSON_OBJECT('a' VALUE 2::text);
|
||||
--SELECT JSON_OBJECT(KEY 'a' VALUE 2::text);
|
||||
SELECT JSON_OBJECT(1::text: 2);
|
||||
SELECT JSON_OBJECT((1::text) VALUE 2);
|
||||
--SELECT JSON_OBJECT(1::text VALUE 2);
|
||||
--SELECT JSON_OBJECT(KEY 1::text VALUE 2);
|
||||
SELECT JSON_OBJECT(json '[1]': 123);
|
||||
SELECT JSON_OBJECT(ARRAY[1,2,3]: 'aaa');
|
||||
|
||||
SELECT JSON_OBJECT(
|
||||
'a': '123',
|
||||
1.23: 123,
|
||||
'c': json '[ 1,true,{ } ]',
|
||||
'd': jsonb '{ "x" : 123.45 }'
|
||||
);
|
||||
|
||||
SELECT JSON_OBJECT(
|
||||
'a': '123',
|
||||
1.23: 123,
|
||||
'c': json '[ 1,true,{ } ]',
|
||||
'd': jsonb '{ "x" : 123.45 }'
|
||||
RETURNING jsonb
|
||||
);
|
||||
|
||||
/*
|
||||
SELECT JSON_OBJECT(
|
||||
'a': '123',
|
||||
KEY 1.23 VALUE 123,
|
||||
'c' VALUE json '[1, true, {}]'
|
||||
);
|
||||
*/
|
||||
|
||||
SELECT JSON_OBJECT('a': '123', 'b': JSON_OBJECT('a': 111, 'b': 'aaa'));
|
||||
SELECT JSON_OBJECT('a': '123', 'b': JSON_OBJECT('a': 111, 'b': 'aaa' RETURNING jsonb));
|
||||
|
||||
SELECT JSON_OBJECT('a': JSON_OBJECT('b': 1 RETURNING text));
|
||||
SELECT JSON_OBJECT('a': JSON_OBJECT('b': 1 RETURNING text) FORMAT JSON);
|
||||
SELECT JSON_OBJECT('a': JSON_OBJECT('b': 1 RETURNING bytea));
|
||||
SELECT JSON_OBJECT('a': JSON_OBJECT('b': 1 RETURNING bytea) FORMAT JSON);
|
||||
|
||||
SELECT JSON_OBJECT('a': '1', 'b': NULL, 'c': 2);
|
||||
SELECT JSON_OBJECT('a': '1', 'b': NULL, 'c': 2 NULL ON NULL);
|
||||
SELECT JSON_OBJECT('a': '1', 'b': NULL, 'c': 2 ABSENT ON NULL);
|
||||
|
||||
SELECT JSON_OBJECT(1: 1, '1': NULL WITH UNIQUE);
|
||||
SELECT JSON_OBJECT(1: 1, '1': NULL ABSENT ON NULL WITH UNIQUE);
|
||||
SELECT JSON_OBJECT(1: 1, '1': NULL NULL ON NULL WITH UNIQUE RETURNING jsonb);
|
||||
SELECT JSON_OBJECT(1: 1, '1': NULL ABSENT ON NULL WITH UNIQUE RETURNING jsonb);
|
||||
|
||||
SELECT JSON_OBJECT(1: 1, '2': NULL, '1': 1 NULL ON NULL WITH UNIQUE);
|
||||
SELECT JSON_OBJECT(1: 1, '2': NULL, '1': 1 ABSENT ON NULL WITH UNIQUE);
|
||||
SELECT JSON_OBJECT(1: 1, '2': NULL, '1': 1 ABSENT ON NULL WITHOUT UNIQUE);
|
||||
SELECT JSON_OBJECT(1: 1, '2': NULL, '1': 1 ABSENT ON NULL WITH UNIQUE RETURNING jsonb);
|
||||
SELECT JSON_OBJECT(1: 1, '2': NULL, '1': 1 ABSENT ON NULL WITHOUT UNIQUE RETURNING jsonb);
|
||||
SELECT JSON_OBJECT(1: 1, '2': NULL, '3': 1, 4: NULL, '5': 'a' ABSENT ON NULL WITH UNIQUE RETURNING jsonb);
|
||||
|
||||
|
||||
-- JSON_ARRAY()
|
||||
SELECT JSON_ARRAY();
|
||||
SELECT JSON_ARRAY(RETURNING json);
|
||||
SELECT JSON_ARRAY(RETURNING json FORMAT JSON);
|
||||
SELECT JSON_ARRAY(RETURNING jsonb);
|
||||
SELECT JSON_ARRAY(RETURNING jsonb FORMAT JSON);
|
||||
SELECT JSON_ARRAY(RETURNING text);
|
||||
SELECT JSON_ARRAY(RETURNING text FORMAT JSON);
|
||||
SELECT JSON_ARRAY(RETURNING text FORMAT JSON ENCODING UTF8);
|
||||
SELECT JSON_ARRAY(RETURNING text FORMAT JSON ENCODING INVALID_ENCODING);
|
||||
SELECT JSON_ARRAY(RETURNING bytea);
|
||||
SELECT JSON_ARRAY(RETURNING bytea FORMAT JSON);
|
||||
SELECT JSON_ARRAY(RETURNING bytea FORMAT JSON ENCODING UTF8);
|
||||
SELECT JSON_ARRAY(RETURNING bytea FORMAT JSON ENCODING UTF16);
|
||||
SELECT JSON_ARRAY(RETURNING bytea FORMAT JSON ENCODING UTF32);
|
||||
|
||||
SELECT JSON_ARRAY('aaa', 111, true, array[1,2,3], NULL, json '{"a": [1]}', jsonb '["a",3]');
|
||||
|
||||
SELECT JSON_ARRAY('a', NULL, 'b' NULL ON NULL);
|
||||
SELECT JSON_ARRAY('a', NULL, 'b' ABSENT ON NULL);
|
||||
SELECT JSON_ARRAY(NULL, NULL, 'b' ABSENT ON NULL);
|
||||
SELECT JSON_ARRAY('a', NULL, 'b' NULL ON NULL RETURNING jsonb);
|
||||
SELECT JSON_ARRAY('a', NULL, 'b' ABSENT ON NULL RETURNING jsonb);
|
||||
SELECT JSON_ARRAY(NULL, NULL, 'b' ABSENT ON NULL RETURNING jsonb);
|
||||
|
||||
SELECT JSON_ARRAY(JSON_ARRAY('{ "a" : 123 }' RETURNING text));
|
||||
SELECT JSON_ARRAY(JSON_ARRAY('{ "a" : 123 }' FORMAT JSON RETURNING text));
|
||||
SELECT JSON_ARRAY(JSON_ARRAY('{ "a" : 123 }' FORMAT JSON RETURNING text) FORMAT JSON);
|
||||
|
||||
SELECT JSON_ARRAY(SELECT i FROM (VALUES (1), (2), (NULL), (4)) foo(i));
|
||||
SELECT JSON_ARRAY(SELECT i FROM (VALUES (NULL::int[]), ('{1,2}'), (NULL), (NULL), ('{3,4}'), (NULL)) foo(i));
|
||||
SELECT JSON_ARRAY(SELECT i FROM (VALUES (NULL::int[]), ('{1,2}'), (NULL), (NULL), ('{3,4}'), (NULL)) foo(i) RETURNING jsonb);
|
||||
--SELECT JSON_ARRAY(SELECT i FROM (VALUES (NULL::int[]), ('{1,2}'), (NULL), (NULL), ('{3,4}'), (NULL)) foo(i) NULL ON NULL);
|
||||
--SELECT JSON_ARRAY(SELECT i FROM (VALUES (NULL::int[]), ('{1,2}'), (NULL), (NULL), ('{3,4}'), (NULL)) foo(i) NULL ON NULL RETURNING jsonb);
|
||||
SELECT JSON_ARRAY(SELECT i FROM (VALUES (3), (1), (NULL), (2)) foo(i) ORDER BY i);
|
||||
-- Should fail
|
||||
SELECT JSON_ARRAY(SELECT FROM (VALUES (1)) foo(i));
|
||||
SELECT JSON_ARRAY(SELECT i, i FROM (VALUES (1)) foo(i));
|
||||
SELECT JSON_ARRAY(SELECT * FROM (VALUES (1, 2)) foo(i, j));
|
||||
|
||||
-- JSON_ARRAYAGG()
|
||||
SELECT JSON_ARRAYAGG(i) IS NULL,
|
||||
JSON_ARRAYAGG(i RETURNING jsonb) IS NULL
|
||||
FROM generate_series(1, 0) i;
|
||||
|
||||
SELECT JSON_ARRAYAGG(i),
|
||||
JSON_ARRAYAGG(i RETURNING jsonb)
|
||||
FROM generate_series(1, 5) i;
|
||||
|
||||
SELECT JSON_ARRAYAGG(i ORDER BY i DESC)
|
||||
FROM generate_series(1, 5) i;
|
||||
|
||||
SELECT JSON_ARRAYAGG(i::text::json)
|
||||
FROM generate_series(1, 5) i;
|
||||
|
||||
SELECT JSON_ARRAYAGG(JSON_ARRAY(i, i + 1 RETURNING text) FORMAT JSON)
|
||||
FROM generate_series(1, 5) i;
|
||||
|
||||
SELECT JSON_ARRAYAGG(NULL),
|
||||
JSON_ARRAYAGG(NULL RETURNING jsonb)
|
||||
FROM generate_series(1, 5);
|
||||
|
||||
SELECT JSON_ARRAYAGG(NULL NULL ON NULL),
|
||||
JSON_ARRAYAGG(NULL NULL ON NULL RETURNING jsonb)
|
||||
FROM generate_series(1, 5);
|
||||
|
||||
SELECT
|
||||
JSON_ARRAYAGG(bar),
|
||||
JSON_ARRAYAGG(bar RETURNING jsonb),
|
||||
JSON_ARRAYAGG(bar ABSENT ON NULL),
|
||||
JSON_ARRAYAGG(bar ABSENT ON NULL RETURNING jsonb),
|
||||
JSON_ARRAYAGG(bar NULL ON NULL),
|
||||
JSON_ARRAYAGG(bar NULL ON NULL RETURNING jsonb),
|
||||
JSON_ARRAYAGG(foo),
|
||||
JSON_ARRAYAGG(foo RETURNING jsonb),
|
||||
JSON_ARRAYAGG(foo ORDER BY bar) FILTER (WHERE bar > 2),
|
||||
JSON_ARRAYAGG(foo ORDER BY bar RETURNING jsonb) FILTER (WHERE bar > 2)
|
||||
FROM
|
||||
(VALUES (NULL), (3), (1), (NULL), (NULL), (5), (2), (4), (NULL)) foo(bar);
|
||||
|
||||
SELECT
|
||||
bar, JSON_ARRAYAGG(bar) FILTER (WHERE bar > 2) OVER (PARTITION BY foo.bar % 2)
|
||||
FROM
|
||||
(VALUES (NULL), (3), (1), (NULL), (NULL), (5), (2), (4), (NULL), (5), (4)) foo(bar);
|
||||
|
||||
-- JSON_OBJECTAGG()
|
||||
SELECT JSON_OBJECTAGG('key': 1) IS NULL,
|
||||
JSON_OBJECTAGG('key': 1 RETURNING jsonb) IS NULL
|
||||
WHERE FALSE;
|
||||
|
||||
SELECT JSON_OBJECTAGG(NULL: 1);
|
||||
|
||||
SELECT JSON_OBJECTAGG(NULL: 1 RETURNING jsonb);
|
||||
|
||||
SELECT
|
||||
JSON_OBJECTAGG(i: i),
|
||||
-- JSON_OBJECTAGG(i VALUE i),
|
||||
-- JSON_OBJECTAGG(KEY i VALUE i),
|
||||
JSON_OBJECTAGG(i: i RETURNING jsonb)
|
||||
FROM
|
||||
generate_series(1, 5) i;
|
||||
|
||||
SELECT
|
||||
JSON_OBJECTAGG(k: v),
|
||||
JSON_OBJECTAGG(k: v NULL ON NULL),
|
||||
JSON_OBJECTAGG(k: v ABSENT ON NULL),
|
||||
JSON_OBJECTAGG(k: v RETURNING jsonb),
|
||||
JSON_OBJECTAGG(k: v NULL ON NULL RETURNING jsonb),
|
||||
JSON_OBJECTAGG(k: v ABSENT ON NULL RETURNING jsonb)
|
||||
FROM
|
||||
(VALUES (1, 1), (1, NULL), (2, NULL), (3, 3)) foo(k, v);
|
||||
|
||||
SELECT JSON_OBJECTAGG(k: v WITH UNIQUE KEYS)
|
||||
FROM (VALUES (1, 1), (1, NULL), (2, 2)) foo(k, v);
|
||||
|
||||
SELECT JSON_OBJECTAGG(k: v ABSENT ON NULL WITH UNIQUE KEYS)
|
||||
FROM (VALUES (1, 1), (1, NULL), (2, 2)) foo(k, v);
|
||||
|
||||
SELECT JSON_OBJECTAGG(k: v ABSENT ON NULL WITH UNIQUE KEYS)
|
||||
FROM (VALUES (1, 1), (0, NULL), (3, NULL), (2, 2), (4, NULL)) foo(k, v);
|
||||
|
||||
SELECT JSON_OBJECTAGG(k: v 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), (1, 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);
|
||||
|
||||
CREATE VIEW json_object_view AS
|
||||
SELECT JSON_OBJECT('foo' : '1' FORMAT JSON, 'bar' : 'baz' RETURNING json);
|
||||
|
||||
\sv json_object_view
|
||||
|
||||
DROP VIEW json_object_view;
|
||||
|
||||
-- Test JSON_ARRAY deparsing
|
||||
EXPLAIN (VERBOSE, COSTS OFF)
|
||||
SELECT JSON_ARRAY('1' FORMAT JSON, 2 RETURNING json);
|
||||
|
||||
CREATE VIEW json_array_view AS
|
||||
SELECT JSON_ARRAY('1' FORMAT JSON, 2 RETURNING json);
|
||||
|
||||
\sv json_array_view
|
||||
|
||||
DROP VIEW json_array_view;
|
||||
|
||||
-- Test JSON_OBJECTAGG deparsing
|
||||
EXPLAIN (VERBOSE, COSTS OFF)
|
||||
SELECT JSON_OBJECTAGG(i: ('111' || i)::bytea FORMAT JSON WITH UNIQUE RETURNING text) FILTER (WHERE i > 3)
|
||||
FROM generate_series(1,5) i;
|
||||
|
||||
EXPLAIN (VERBOSE, COSTS OFF)
|
||||
SELECT JSON_OBJECTAGG(i: ('111' || i)::bytea FORMAT JSON WITH UNIQUE RETURNING text) OVER (PARTITION BY i % 2)
|
||||
FROM generate_series(1,5) i;
|
||||
|
||||
CREATE VIEW json_objectagg_view AS
|
||||
SELECT JSON_OBJECTAGG(i: ('111' || i)::bytea FORMAT JSON WITH UNIQUE RETURNING text) FILTER (WHERE i > 3)
|
||||
FROM generate_series(1,5) i;
|
||||
|
||||
\sv json_objectagg_view
|
||||
|
||||
DROP VIEW json_objectagg_view;
|
||||
|
||||
-- Test JSON_ARRAYAGG deparsing
|
||||
EXPLAIN (VERBOSE, COSTS OFF)
|
||||
SELECT JSON_ARRAYAGG(('111' || i)::bytea FORMAT JSON NULL ON NULL RETURNING text) FILTER (WHERE i > 3)
|
||||
FROM generate_series(1,5) i;
|
||||
|
||||
EXPLAIN (VERBOSE, COSTS OFF)
|
||||
SELECT JSON_ARRAYAGG(('111' || i)::bytea FORMAT JSON NULL ON NULL RETURNING text) OVER (PARTITION BY i % 2)
|
||||
FROM generate_series(1,5) i;
|
||||
|
||||
CREATE VIEW json_arrayagg_view AS
|
||||
SELECT JSON_ARRAYAGG(('111' || i)::bytea FORMAT JSON NULL ON NULL RETURNING text) FILTER (WHERE i > 3)
|
||||
FROM generate_series(1,5) i;
|
||||
|
||||
\sv json_arrayagg_view
|
||||
|
||||
DROP VIEW json_arrayagg_view;
|
||||
|
||||
-- Test JSON_ARRAY(subquery) deparsing
|
||||
EXPLAIN (VERBOSE, COSTS OFF)
|
||||
SELECT JSON_ARRAY(SELECT i FROM (VALUES (1), (2), (NULL), (4)) foo(i) RETURNING jsonb);
|
||||
|
||||
CREATE VIEW json_array_subquery_view AS
|
||||
SELECT JSON_ARRAY(SELECT i FROM (VALUES (1), (2), (NULL), (4)) foo(i) RETURNING jsonb);
|
||||
|
||||
\sv json_array_subquery_view
|
||||
|
||||
DROP VIEW json_array_subquery_view;
|
Loading…
x
Reference in New Issue
Block a user