Content-Length: 874671 | pFad | http://github.com/postgrespro/postgres/commit/9fb0b797c261058e29e8e65f3c0f4e8c1f48035a

A7 Add JSON_TRANSFORM() · postgrespro/postgres@9fb0b79 · GitHub
Skip to content

Commit 9fb0b79

Browse files
author
Nikita Glukhov
committed
Add JSON_TRANSFORM()
1 parent 740222f commit 9fb0b79

File tree

22 files changed

+2239
-67
lines changed

22 files changed

+2239
-67
lines changed

src/backend/catalog/system_functions.sql

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -579,6 +579,15 @@ LANGUAGE INTERNAL
579579
STRICT STABLE PARALLEL SAFE
580580
AS 'jsonb_path_query_first_tz';
581581

582+
CREATE OR REPLACE FUNCTION
583+
jsonb_path_set(target jsonb, path jsonpath, newval jsonb,
584+
vars jsonb DEFAULT '{}',
585+
silent boolean DEFAULT false)
586+
RETURNS jsonb
587+
LANGUAGE INTERNAL
588+
IMMUTABLE PARALLEL SAFE
589+
AS 'jsonb_path_set';
590+
582591
-- default normalization form is NFC, per SQL standard
583592
CREATE OR REPLACE FUNCTION
584593
"normalize"(text, text DEFAULT 'NFC')

src/backend/executor/execExpr.c

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2668,6 +2668,101 @@ ExecInitExprRec(Expr *node, ExprState *state,
26682668
break;
26692669
}
26702670

2671+
case T_JsonTransformExpr:
2672+
{
2673+
JsonTransformExpr *jexpr = castNode(JsonTransformExpr, node);
2674+
ListCell *argexprlc;
2675+
ListCell *argnamelc;
2676+
ListCell *oplc;
2677+
int i = 0;
2678+
2679+
scratch.opcode = EEOP_JSONTRANSFORM;
2680+
scratch.d.jsontransform.jsexpr = jexpr;
2681+
2682+
scratch.d.jsontransform.formatted_expr =
2683+
palloc(sizeof(*scratch.d.jsontransform.formatted_expr));
2684+
2685+
ExecInitExprRec((Expr *) jexpr->formatted_expr, state,
2686+
&scratch.d.jsontransform.formatted_expr->value,
2687+
&scratch.d.jsontransform.formatted_expr->isnull);
2688+
2689+
scratch.d.jsontransform.ops =
2690+
palloc(sizeof(*scratch.d.jsontransform.ops) * list_length(jexpr->ops));
2691+
2692+
foreach(oplc, jexpr->ops)
2693+
{
2694+
JsonTransformOp *op = lfirst_node(JsonTransformOp, oplc);
2695+
2696+
if (op->expr)
2697+
{
2698+
ExecInitExprRec((Expr *) op->expr, state,
2699+
&scratch.d.jsontransform.ops[i].expr.value,
2700+
&scratch.d.jsontransform.ops[i].expr.isnull);
2701+
2702+
scratch.d.jsontransform.ops[i].expr_typid = exprType(op->expr);
2703+
scratch.d.jsontransform.ops[i].expr_typmod = exprTypmod(op->expr);
2704+
}
2705+
else
2706+
{
2707+
memset(&scratch.d.jsontransform.ops[i], 0,
2708+
sizeof(scratch.d.jsontransform.ops[i]));
2709+
scratch.d.jsontransform.ops[i].expr.isnull = true;
2710+
}
2711+
2712+
ExecInitExprRec((Expr *) op->pathspec, state,
2713+
&scratch.d.jsontransform.ops[i].pathspec.value,
2714+
&scratch.d.jsontransform.ops[i].pathspec.isnull);
2715+
2716+
i++;
2717+
}
2718+
2719+
scratch.d.jsontransform.res_expr =
2720+
palloc(sizeof(*scratch.d.jsontransform.res_expr));
2721+
2722+
scratch.d.jsontransform.result_expr = jexpr->result_coercion
2723+
? ExecInitExprWithCaseValue((Expr *) jexpr->result_coercion->expr,
2724+
state->parent,
2725+
&scratch.d.jsontransform.res_expr->value,
2726+
&scratch.d.jsontransform.res_expr->isnull)
2727+
: NULL;
2728+
2729+
if (jexpr->result_coercion && jexpr->result_coercion->via_io)
2730+
{
2731+
Oid typinput;
2732+
2733+
/* lookup the result type's input function */
2734+
getTypeInputInfo(jexpr->returning->typid, &typinput,
2735+
&scratch.d.jsontransform.input.typioparam);
2736+
fmgr_info(typinput, &scratch.d.jsontransform.input.func);
2737+
}
2738+
2739+
scratch.d.jsontransform.args = NIL;
2740+
2741+
forboth(argexprlc, jexpr->passing_values,
2742+
argnamelc, jexpr->passing_names)
2743+
{
2744+
Expr *argexpr = (Expr *) lfirst(argexprlc);
2745+
String *argname = lfirst_node(String, argnamelc);
2746+
JsonPathVariableEvalContext *var = palloc(sizeof(*var));
2747+
2748+
var->name = pstrdup(argname->sval);
2749+
var->typid = exprType((Node *) argexpr);
2750+
var->typmod = exprTypmod((Node *) argexpr);
2751+
var->estate = ExecInitExpr(argexpr, state->parent);
2752+
var->econtext = NULL;
2753+
var->mcxt = NULL;
2754+
var->evaluated = false;
2755+
var->value = (Datum) 0;
2756+
var->isnull = true;
2757+
2758+
scratch.d.jsontransform.args =
2759+
lappend(scratch.d.jsontransform.args, var);
2760+
}
2761+
2762+
ExprEvalPushStep(state, &scratch);
2763+
break;
2764+
}
2765+
26712766
default:
26722767
elog(ERROR, "unrecognized node type: %d",
26732768
(int) nodeTag(node));

src/backend/executor/execExprInterp.c

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -491,6 +491,7 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
491491
&&CASE_EEOP_JSON_CONSTRUCTOR,
492492
&&CASE_EEOP_IS_JSON,
493493
&&CASE_EEOP_JSONEXPR,
494+
&&CASE_EEOP_JSONTRANSFORM,
494495
&&CASE_EEOP_AGG_STRICT_DESERIALIZE,
495496
&&CASE_EEOP_AGG_DESERIALIZE,
496497
&&CASE_EEOP_AGG_STRICT_INPUT_CHECK_ARGS,
@@ -1824,6 +1825,13 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
18241825
EEO_NEXT();
18251826
}
18261827

1828+
EEO_CASE(EEOP_JSONTRANSFORM)
1829+
{
1830+
/* too complex for an inline implementation */
1831+
ExecEvalJsonTransform(state, op, econtext);
1832+
EEO_NEXT();
1833+
}
1834+
18271835
EEO_CASE(EEOP_LAST)
18281836
{
18291837
/* unreachable */
@@ -5143,3 +5151,111 @@ ExecEvalJson(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
51435151

51445152
*op->resvalue = res;
51455153
}
5154+
5155+
/*
5156+
* Evaluate a coercion of a JSON item to the target type.
5157+
*/
5158+
static Datum
5159+
ExecEvalJsonTransformCoercion(ExprEvalStep *op, ExprContext *econtext,
5160+
Datum res, bool *isNull)
5161+
{
5162+
JsonTransformExpr *jexpr = op->d.jsontransform.jsexpr;
5163+
JsonCoercion *coercion = jexpr->result_coercion;
5164+
5165+
if (coercion)
5166+
{
5167+
if (coercion->via_io)
5168+
{
5169+
/* strip quotes and call typinput function */
5170+
char *str = *isNull ? NULL : JsonbUnquote(DatumGetJsonbP(res));
5171+
5172+
return InputFunctionCall(&op->d.jsontransform.input.func, str,
5173+
op->d.jsontransform.input.typioparam,
5174+
jexpr->returning->typmod);
5175+
}
5176+
5177+
if (coercion->via_populate)
5178+
return json_populate_type(res, JSONBOID,
5179+
jexpr->returning->typid,
5180+
jexpr->returning->typmod,
5181+
&op->d.jsontransform.cache,
5182+
econtext->ecxt_per_query_memory,
5183+
isNull);
5184+
}
5185+
5186+
if (op->d.jsontransform.result_expr)
5187+
{
5188+
op->d.jsontransform.res_expr->value = res;
5189+
op->d.jsontransform.res_expr->isnull = *isNull;
5190+
5191+
res = ExecEvalExpr(op->d.jsontransform.result_expr, econtext, isNull);
5192+
}
5193+
5194+
return res;
5195+
}
5196+
5197+
void
5198+
ExecEvalJsonTransform(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
5199+
{
5200+
JsonTransformExpr *jexpr = op->d.jsontransform.jsexpr;
5201+
Datum res = (Datum) 0;
5202+
ListCell *lc;
5203+
int i = 0;
5204+
5205+
*op->resnull = true; /* until we get a result */
5206+
*op->resvalue = (Datum) 0;
5207+
5208+
if (op->d.jsontransform.formatted_expr->isnull)
5209+
goto check_null;
5210+
5211+
res = op->d.jsontransform.formatted_expr->value;
5212+
5213+
/* reset JSON path variable contexts */
5214+
foreach(lc, op->d.jsontransform.args)
5215+
{
5216+
JsonPathVariableEvalContext *var = lfirst(lc);
5217+
5218+
var->econtext = econtext;
5219+
var->evaluated = false;
5220+
}
5221+
5222+
foreach(lc, jexpr->ops)
5223+
{
5224+
JsonPath *path;
5225+
JsonTransformOp *oper = lfirst_node(JsonTransformOp, lc);
5226+
5227+
if (op->d.jsontransform.ops[i].pathspec.isnull)
5228+
goto check_null;
5229+
5230+
if (oper->on_null == JSTB_ERROR &&
5231+
op->d.jsontransform.ops[i].expr.isnull)
5232+
ereport(ERROR,
5233+
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
5234+
errmsg("JSON_TRANSFORM called with NULL input")));
5235+
5236+
path = DatumGetJsonPathP(op->d.jsontransform.ops[i].pathspec.value);
5237+
res = JsonPathTransform(res, path,
5238+
op->d.jsontransform.ops[i].expr.value,
5239+
op->d.jsontransform.ops[i].expr.isnull,
5240+
op->d.jsontransform.ops[i].expr_typid,
5241+
op->d.jsontransform.ops[i].expr_typmod,
5242+
op->d.jsontransform.args,
5243+
oper->op_type, oper->on_existing,
5244+
oper->on_missing, oper->on_null);
5245+
5246+
*op->resnull = res == (Datum) 0;
5247+
5248+
if (*op->resnull)
5249+
goto check_null;
5250+
5251+
i++;
5252+
}
5253+
5254+
*op->resvalue = ExecEvalJsonTransformCoercion(op, econtext, res, op->resnull);
5255+
return;
5256+
5257+
check_null:
5258+
/* execute domain checks for NULLs */
5259+
(void) ExecEvalJsonTransformCoercion(op, econtext, res, op->resnull);
5260+
Assert(*op->resnull);
5261+
}

src/backend/nodes/makefuncs.c

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -941,3 +941,29 @@ makeJsonIsPredicate(Node *expr, JsonFormat *format, JsonValueType item_type,
941941

942942
return (Node *) n;
943943
}
944+
945+
/*
946+
* makeJsonTransformOp -
947+
* creates a JsonTransformOp node
948+
*/
949+
Node *
950+
makeJsonTransformOp(JsonTransformOpType op_type,
951+
Node *pathspec, Node *expr,
952+
JsonTransformBehavior on_existing,
953+
JsonTransformBehavior on_missing,
954+
JsonTransformBehavior on_null,
955+
int location)
956+
{
957+
JsonTransformOp *n = makeNode(JsonTransformOp);
958+
959+
n->pathspec = pathspec;
960+
n->expr = expr;
961+
n->op_type = op_type;
962+
n->on_existing = on_existing;
963+
n->on_missing = on_missing;
964+
n->on_null = on_null;
965+
n->location = location;
966+
967+
return (Node *) n;
968+
}
969+

0 commit comments

Comments
 (0)








ApplySandwichStrip

pFad - (p)hone/(F)rame/(a)nonymizer/(d)eclutterfier!      Saves Data!


--- a PPN by Garber Painting Akron. With Image Size Reduction included!

Fetched URL: http://github.com/postgrespro/postgres/commit/9fb0b797c261058e29e8e65f3c0f4e8c1f48035a

Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy