Skip to content

Commit 7081ac4

Browse files
committed
SQL/JSON: add standard JSON constructor functions
This commit introduces the SQL/JSON standard-conforming constructors for JSON types: JSON_ARRAY() JSON_ARRAYAGG() JSON_OBJECT() JSON_OBJECTAGG() Most of the functionality was already present in PostgreSQL-specific functions, but these include some new functionality such as the ability to skip or include NULL values, and to allow duplicate keys or throw error when they are found, as well as the standard specified syntax to specify output type and format. Author: Nikita Glukhov <n.gluhov@postgrespro.ru> Author: Teodor Sigaev <teodor@sigaev.ru> Author: Oleg Bartunov <obartunov@gmail.com> Author: Alexander Korotkov <aekorotkov@gmail.com> Author: Amit Langote <amitlangote09@gmail.com> 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/CAF4Au4w2x-5LTnN_bxky-mq4=WOqsGsxSpENCzHRAzSnEd8+WQ@mail.gmail.com Discussion: https://postgr.es/m/cd0bb935-0158-78a7-08b5-904886deac4b@postgrespro.ru Discussion: https://postgr.es/m/20220616233130.rparivafipt6doj3@alap3.anarazel.de Discussion: https://postgr.es/m/abd9b83b-aa66-f230-3d6d-734817f0995d%40postgresql.org
1 parent 38b7437 commit 7081ac4

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+4470
-141
lines changed

doc/src/sgml/func.sgml

Lines changed: 283 additions & 3 deletions
Large diffs are not rendered by default.

src/backend/executor/execExpr.c

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2279,6 +2279,97 @@ ExecInitExprRec(Expr *node, ExprState *state,
22792279
break;
22802280
}
22812281

2282+
case T_JsonValueExpr:
2283+
{
2284+
JsonValueExpr *jve = (JsonValueExpr *) node;
2285+
2286+
ExecInitExprRec(jve->raw_expr, state, resv, resnull);
2287+
2288+
if (jve->formatted_expr)
2289+
{
2290+
Datum *innermost_caseval = state->innermost_caseval;
2291+
bool *innermost_isnull = state->innermost_casenull;
2292+
2293+
state->innermost_caseval = resv;
2294+
state->innermost_casenull = resnull;
2295+
2296+
ExecInitExprRec(jve->formatted_expr, state, resv, resnull);
2297+
2298+
state->innermost_caseval = innermost_caseval;
2299+
state->innermost_casenull = innermost_isnull;
2300+
}
2301+
break;
2302+
}
2303+
2304+
case T_JsonConstructorExpr:
2305+
{
2306+
JsonConstructorExpr *ctor = (JsonConstructorExpr *) node;
2307+
List *args = ctor->args;
2308+
ListCell *lc;
2309+
int nargs = list_length(args);
2310+
int argno = 0;
2311+
2312+
if (ctor->func)
2313+
{
2314+
ExecInitExprRec(ctor->func, state, resv, resnull);
2315+
}
2316+
else
2317+
{
2318+
JsonConstructorExprState *jcstate;
2319+
2320+
jcstate = palloc0(sizeof(JsonConstructorExprState));
2321+
2322+
scratch.opcode = EEOP_JSON_CONSTRUCTOR;
2323+
scratch.d.json_constructor.jcstate = jcstate;
2324+
2325+
jcstate->constructor = ctor;
2326+
jcstate->arg_values = (Datum *) palloc(sizeof(Datum) * nargs);
2327+
jcstate->arg_nulls = (bool *) palloc(sizeof(bool) * nargs);
2328+
jcstate->arg_types = (Oid *) palloc(sizeof(Oid) * nargs);
2329+
jcstate->nargs = nargs;
2330+
2331+
foreach(lc, args)
2332+
{
2333+
Expr *arg = (Expr *) lfirst(lc);
2334+
2335+
jcstate->arg_types[argno] = exprType((Node *) arg);
2336+
2337+
if (IsA(arg, Const))
2338+
{
2339+
/* Don't evaluate const arguments every round */
2340+
Const *con = (Const *) arg;
2341+
2342+
jcstate->arg_values[argno] = con->constvalue;
2343+
jcstate->arg_nulls[argno] = con->constisnull;
2344+
}
2345+
else
2346+
{
2347+
ExecInitExprRec(arg, state,
2348+
&jcstate->arg_values[argno],
2349+
&jcstate->arg_nulls[argno]);
2350+
}
2351+
argno++;
2352+
}
2353+
2354+
ExprEvalPushStep(state, &scratch);
2355+
}
2356+
2357+
if (ctor->coercion)
2358+
{
2359+
Datum *innermost_caseval = state->innermost_caseval;
2360+
bool *innermost_isnull = state->innermost_casenull;
2361+
2362+
state->innermost_caseval = resv;
2363+
state->innermost_casenull = resnull;
2364+
2365+
ExecInitExprRec(ctor->coercion, state, resv, resnull);
2366+
2367+
state->innermost_caseval = innermost_caseval;
2368+
state->innermost_casenull = innermost_isnull;
2369+
}
2370+
}
2371+
break;
2372+
22822373
case T_NullTest:
22832374
{
22842375
NullTest *ntest = (NullTest *) node;

src/backend/executor/execExprInterp.c

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,8 @@
7171
#include "utils/date.h"
7272
#include "utils/datum.h"
7373
#include "utils/expandedrecord.h"
74+
#include "utils/json.h"
75+
#include "utils/jsonb.h"
7476
#include "utils/lsyscache.h"
7577
#include "utils/memutils.h"
7678
#include "utils/timestamp.h"
@@ -474,6 +476,7 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
474476
&&CASE_EEOP_SCALARARRAYOP,
475477
&&CASE_EEOP_HASHED_SCALARARRAYOP,
476478
&&CASE_EEOP_XMLEXPR,
479+
&&CASE_EEOP_JSON_CONSTRUCTOR,
477480
&&CASE_EEOP_AGGREF,
478481
&&CASE_EEOP_GROUPING_FUNC,
479482
&&CASE_EEOP_WINDOW_FUNC,
@@ -1511,6 +1514,13 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
15111514
EEO_NEXT();
15121515
}
15131516

1517+
EEO_CASE(EEOP_JSON_CONSTRUCTOR)
1518+
{
1519+
/* too complex for an inline implementation */
1520+
ExecEvalJsonConstructor(state, op, econtext);
1521+
EEO_NEXT();
1522+
}
1523+
15141524
EEO_CASE(EEOP_AGGREF)
15151525
{
15161526
/*
@@ -4437,3 +4447,43 @@ ExecAggPlainTransByRef(AggState *aggstate, AggStatePerTrans pertrans,
44374447

44384448
MemoryContextSwitchTo(oldContext);
44394449
}
4450+
4451+
/*
4452+
* Evaluate a JSON constructor expression.
4453+
*/
4454+
void
4455+
ExecEvalJsonConstructor(ExprState *state, ExprEvalStep *op,
4456+
ExprContext *econtext)
4457+
{
4458+
Datum res;
4459+
JsonConstructorExprState *jcstate = op->d.json_constructor.jcstate;
4460+
JsonConstructorExpr *ctor = jcstate->constructor;
4461+
bool is_jsonb = ctor->returning->format->format_type == JS_FORMAT_JSONB;
4462+
bool isnull = false;
4463+
4464+
if (ctor->type == JSCTOR_JSON_ARRAY)
4465+
res = (is_jsonb ?
4466+
jsonb_build_array_worker :
4467+
json_build_array_worker) (jcstate->nargs,
4468+
jcstate->arg_values,
4469+
jcstate->arg_nulls,
4470+
jcstate->arg_types,
4471+
jcstate->constructor->absent_on_null);
4472+
else if (ctor->type == JSCTOR_JSON_OBJECT)
4473+
res = (is_jsonb ?
4474+
jsonb_build_object_worker :
4475+
json_build_object_worker) (jcstate->nargs,
4476+
jcstate->arg_values,
4477+
jcstate->arg_nulls,
4478+
jcstate->arg_types,
4479+
jcstate->constructor->absent_on_null,
4480+
jcstate->constructor->unique);
4481+
else
4482+
{
4483+
res = (Datum) 0;
4484+
elog(ERROR, "invalid JsonConstructorExpr type %d", ctor->type);
4485+
}
4486+
4487+
*op->resvalue = res;
4488+
*op->resnull = isnull;
4489+
}

src/backend/jit/llvm/llvmjit_expr.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1842,6 +1842,12 @@ llvm_compile_expr(ExprState *state)
18421842
LLVMBuildBr(b, opblocks[opno + 1]);
18431843
break;
18441844

1845+
case EEOP_JSON_CONSTRUCTOR:
1846+
build_EvalXFunc(b, mod, "ExecEvalJsonConstructor",
1847+
v_state, op, v_econtext);
1848+
LLVMBuildBr(b, opblocks[opno + 1]);
1849+
break;
1850+
18451851
case EEOP_AGGREF:
18461852
{
18471853
LLVMValueRef v_aggno;

src/backend/jit/llvm/llvmjit_types.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ void *referenced_functions[] =
132132
ExecEvalSysVar,
133133
ExecEvalWholeRowVar,
134134
ExecEvalXmlExpr,
135+
ExecEvalJsonConstructor,
135136
MakeExpandedObjectReadOnlyInternal,
136137
slot_getmissingattrs,
137138
slot_getsomeattrs_int,

src/backend/nodes/makefuncs.c

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "catalog/pg_type.h"
2020
#include "nodes/makefuncs.h"
2121
#include "nodes/nodeFuncs.h"
22+
#include "utils/errcodes.h"
2223
#include "utils/lsyscache.h"
2324

2425

@@ -825,3 +826,71 @@ makeVacuumRelation(RangeVar *relation, Oid oid, List *va_cols)
825826
v->va_cols = va_cols;
826827
return v;
827828
}
829+
830+
/*
831+
* makeJsonFormat -
832+
* creates a JsonFormat node
833+
*/
834+
JsonFormat *
835+
makeJsonFormat(JsonFormatType type, JsonEncoding encoding, int location)
836+
{
837+
JsonFormat *jf = makeNode(JsonFormat);
838+
839+
jf->format_type = type;
840+
jf->encoding = encoding;
841+
jf->location = location;
842+
843+
return jf;
844+
}
845+
846+
/*
847+
* makeJsonValueExpr -
848+
* creates a JsonValueExpr node
849+
*/
850+
JsonValueExpr *
851+
makeJsonValueExpr(Expr *expr, JsonFormat *format)
852+
{
853+
JsonValueExpr *jve = makeNode(JsonValueExpr);
854+
855+
jve->raw_expr = expr;
856+
jve->formatted_expr = NULL;
857+
jve->format = format;
858+
859+
return jve;
860+
}
861+
862+
/*
863+
* makeJsonEncoding -
864+
* converts JSON encoding name to enum JsonEncoding
865+
*/
866+
JsonEncoding
867+
makeJsonEncoding(char *name)
868+
{
869+
if (!pg_strcasecmp(name, "utf8"))
870+
return JS_ENC_UTF8;
871+
if (!pg_strcasecmp(name, "utf16"))
872+
return JS_ENC_UTF16;
873+
if (!pg_strcasecmp(name, "utf32"))
874+
return JS_ENC_UTF32;
875+
876+
ereport(ERROR,
877+
errcode(ERRCODE_INVALID_PARAMETER_VALUE),
878+
errmsg("unrecognized JSON encoding: %s", name));
879+
880+
return JS_ENC_DEFAULT;
881+
}
882+
883+
/*
884+
* makeJsonKeyValue -
885+
* creates a JsonKeyValue node
886+
*/
887+
Node *
888+
makeJsonKeyValue(Node *key, Node *value)
889+
{
890+
JsonKeyValue *n = makeNode(JsonKeyValue);
891+
892+
n->key = (Expr *) key;
893+
n->value = castNode(JsonValueExpr, value);
894+
895+
return (Node *) n;
896+
}

0 commit comments

Comments
 (0)