PostgreSQL Source Code git master
jsonbsubs.c
Go to the documentation of this file.
1/*-------------------------------------------------------------------------
2 *
3 * jsonbsubs.c
4 * Subscripting support functions for jsonb.
5 *
6 * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
8 *
9 *
10 * IDENTIFICATION
11 * src/backend/utils/adt/jsonbsubs.c
12 *
13 *-------------------------------------------------------------------------
14 */
15#include "postgres.h"
16
17#include "executor/execExpr.h"
18#include "nodes/nodeFuncs.h"
19#include "nodes/subscripting.h"
20#include "parser/parse_coerce.h"
21#include "parser/parse_expr.h"
22#include "utils/builtins.h"
23#include "utils/jsonb.h"
24
25
26/* SubscriptingRefState.workspace for jsonb subscripting execution */
27typedef struct JsonbSubWorkspace
28{
29 bool expectArray; /* jsonb root is expected to be an array */
30 Oid *indexOid; /* OID of coerced subscript expression, could
31 * be only integer or text */
32 Datum *index; /* Subscript values in Datum format */
34
35
36/*
37 * Finish parse analysis of a SubscriptingRef expression for a jsonb.
38 *
39 * Transform the subscript expressions, coerce them to text,
40 * and determine the result type of the SubscriptingRef node.
41 */
42static void
44 List *indirection,
45 ParseState *pstate,
46 bool isSlice,
47 bool isAssignment)
48{
49 List *upperIndexpr = NIL;
51
52 /*
53 * Transform and convert the subscript expressions. Jsonb subscripting
54 * does not support slices, look only and the upper index.
55 */
56 foreach(idx, indirection)
57 {
59 Node *subExpr;
60
61 if (isSlice)
62 {
63 Node *expr = ai->uidx ? ai->uidx : ai->lidx;
64
66 (errcode(ERRCODE_DATATYPE_MISMATCH),
67 errmsg("jsonb subscript does not support slices"),
68 parser_errposition(pstate, exprLocation(expr))));
69 }
70
71 if (ai->uidx)
72 {
73 Oid subExprType = InvalidOid,
74 targetType = UNKNOWNOID;
75
76 subExpr = transformExpr(pstate, ai->uidx, pstate->p_expr_kind);
77 subExprType = exprType(subExpr);
78
79 if (subExprType != UNKNOWNOID)
80 {
81 Oid targets[2] = {INT4OID, TEXTOID};
82
83 /*
84 * Jsonb can handle multiple subscript types, but cases when a
85 * subscript could be coerced to multiple target types must be
86 * avoided, similar to overloaded functions. It could be
87 * possibly extend with jsonpath in the future.
88 */
89 for (int i = 0; i < 2; i++)
90 {
91 if (can_coerce_type(1, &subExprType, &targets[i], COERCION_IMPLICIT))
92 {
93 /*
94 * One type has already succeeded, it means there are
95 * two coercion targets possible, failure.
96 */
97 if (targetType != UNKNOWNOID)
99 (errcode(ERRCODE_DATATYPE_MISMATCH),
100 errmsg("subscript type %s is not supported", format_type_be(subExprType)),
101 errhint("jsonb subscript must be coercible to only one type, integer or text."),
102 parser_errposition(pstate, exprLocation(subExpr))));
103
104 targetType = targets[i];
105 }
106 }
107
108 /*
109 * No suitable types were found, failure.
110 */
111 if (targetType == UNKNOWNOID)
113 (errcode(ERRCODE_DATATYPE_MISMATCH),
114 errmsg("subscript type %s is not supported", format_type_be(subExprType)),
115 errhint("jsonb subscript must be coercible to either integer or text."),
116 parser_errposition(pstate, exprLocation(subExpr))));
117 }
118 else
119 targetType = TEXTOID;
120
121 /*
122 * We known from can_coerce_type that coercion will succeed, so
123 * coerce_type could be used. Note the implicit coercion context,
124 * which is required to handle subscripts of different types,
125 * similar to overloaded functions.
126 */
127 subExpr = coerce_type(pstate,
128 subExpr, subExprType,
129 targetType, -1,
132 -1);
133 if (subExpr == NULL)
135 (errcode(ERRCODE_DATATYPE_MISMATCH),
136 errmsg("jsonb subscript must have text type"),
137 parser_errposition(pstate, exprLocation(subExpr))));
138 }
139 else
140 {
141 /*
142 * Slice with omitted upper bound. Should not happen as we already
143 * errored out on slice earlier, but handle this just in case.
144 */
145 Assert(isSlice && ai->is_slice);
147 (errcode(ERRCODE_DATATYPE_MISMATCH),
148 errmsg("jsonb subscript does not support slices"),
149 parser_errposition(pstate, exprLocation(ai->uidx))));
150 }
151
152 upperIndexpr = lappend(upperIndexpr, subExpr);
153 }
154
155 /* store the transformed lists into the SubscriptRef node */
156 sbsref->refupperindexpr = upperIndexpr;
157 sbsref->reflowerindexpr = NIL;
158
159 /* Determine the result type of the subscripting operation; always jsonb */
160 sbsref->refrestype = JSONBOID;
161 sbsref->reftypmod = -1;
162}
163
164/*
165 * During execution, process the subscripts in a SubscriptingRef expression.
166 *
167 * The subscript expressions are already evaluated in Datum form in the
168 * SubscriptingRefState's arrays. Check and convert them as necessary.
169 *
170 * If any subscript is NULL, we throw error in assignment cases, or in fetch
171 * cases set result to NULL and return false (instructing caller to skip the
172 * rest of the SubscriptingRef sequence).
173 */
174static bool
176 ExprEvalStep *op,
177 ExprContext *econtext)
178{
179 SubscriptingRefState *sbsrefstate = op->d.sbsref_subscript.state;
180 JsonbSubWorkspace *workspace = (JsonbSubWorkspace *) sbsrefstate->workspace;
181
182 /*
183 * In case if the first subscript is an integer, the source jsonb is
184 * expected to be an array. This information is not used directly, all
185 * such cases are handled within corresponding jsonb assign functions. But
186 * if the source jsonb is NULL the expected type will be used to construct
187 * an empty source.
188 */
189 if (sbsrefstate->numupper > 0 && sbsrefstate->upperprovided[0] &&
190 !sbsrefstate->upperindexnull[0] && workspace->indexOid[0] == INT4OID)
191 workspace->expectArray = true;
192
193 /* Process upper subscripts */
194 for (int i = 0; i < sbsrefstate->numupper; i++)
195 {
196 if (sbsrefstate->upperprovided[i])
197 {
198 /* If any index expr yields NULL, result is NULL or error */
199 if (sbsrefstate->upperindexnull[i])
200 {
201 if (sbsrefstate->isassignment)
203 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
204 errmsg("jsonb subscript in assignment must not be null")));
205 *op->resnull = true;
206 return false;
207 }
208
209 /*
210 * For jsonb fetch and assign functions we need to provide path in
211 * text format. Convert if it's not already text.
212 */
213 if (workspace->indexOid[i] == INT4OID)
214 {
215 Datum datum = sbsrefstate->upperindex[i];
216 char *cs = DatumGetCString(DirectFunctionCall1(int4out, datum));
217
218 workspace->index[i] = CStringGetTextDatum(cs);
219 }
220 else
221 workspace->index[i] = sbsrefstate->upperindex[i];
222 }
223 }
224
225 return true;
226}
227
228/*
229 * Evaluate SubscriptingRef fetch for a jsonb element.
230 *
231 * Source container is in step's result variable (it's known not NULL, since
232 * we set fetch_strict to true).
233 */
234static void
236 ExprEvalStep *op,
237 ExprContext *econtext)
238{
239 SubscriptingRefState *sbsrefstate = op->d.sbsref.state;
240 JsonbSubWorkspace *workspace = (JsonbSubWorkspace *) sbsrefstate->workspace;
241 Jsonb *jsonbSource;
242
243 /* Should not get here if source jsonb (or any subscript) is null */
244 Assert(!(*op->resnull));
245
246 jsonbSource = DatumGetJsonbP(*op->resvalue);
247 *op->resvalue = jsonb_get_element(jsonbSource,
248 workspace->index,
249 sbsrefstate->numupper,
250 op->resnull,
251 false);
252}
253
254/*
255 * Evaluate SubscriptingRef assignment for a jsonb element assignment.
256 *
257 * Input container (possibly null) is in result area, replacement value is in
258 * SubscriptingRefState's replacevalue/replacenull.
259 */
260static void
262 ExprEvalStep *op,
263 ExprContext *econtext)
264{
265 SubscriptingRefState *sbsrefstate = op->d.sbsref.state;
266 JsonbSubWorkspace *workspace = (JsonbSubWorkspace *) sbsrefstate->workspace;
267 Jsonb *jsonbSource;
268 JsonbValue replacevalue;
269
270 if (sbsrefstate->replacenull)
271 replacevalue.type = jbvNull;
272 else
274 &replacevalue);
275
276 /*
277 * In case if the input container is null, set up an empty jsonb and
278 * proceed with the assignment.
279 */
280 if (*op->resnull)
281 {
282 JsonbValue newSource;
283
284 /*
285 * To avoid any surprising results, set up an empty jsonb array in
286 * case of an array is expected (i.e. the first subscript is integer),
287 * otherwise jsonb object.
288 */
289 if (workspace->expectArray)
290 {
291 newSource.type = jbvArray;
292 newSource.val.array.nElems = 0;
293 newSource.val.array.rawScalar = false;
294 }
295 else
296 {
297 newSource.type = jbvObject;
298 newSource.val.object.nPairs = 0;
299 }
300
301 jsonbSource = JsonbValueToJsonb(&newSource);
302 *op->resnull = false;
303 }
304 else
305 jsonbSource = DatumGetJsonbP(*op->resvalue);
306
307 *op->resvalue = jsonb_set_element(jsonbSource,
308 workspace->index,
309 sbsrefstate->numupper,
310 &replacevalue);
311 /* The result is never NULL, so no need to change *op->resnull */
312}
313
314/*
315 * Compute old jsonb element value for a SubscriptingRef assignment
316 * expression. Will only be called if the new-value subexpression
317 * contains SubscriptingRef or FieldStore. This is the same as the
318 * regular fetch case, except that we have to handle a null jsonb,
319 * and the value should be stored into the SubscriptingRefState's
320 * prevvalue/prevnull fields.
321 */
322static void
324 ExprEvalStep *op,
325 ExprContext *econtext)
326{
327 SubscriptingRefState *sbsrefstate = op->d.sbsref.state;
328
329 if (*op->resnull)
330 {
331 /* whole jsonb is null, so any element is too */
332 sbsrefstate->prevvalue = (Datum) 0;
333 sbsrefstate->prevnull = true;
334 }
335 else
336 {
337 Jsonb *jsonbSource = DatumGetJsonbP(*op->resvalue);
338
339 sbsrefstate->prevvalue = jsonb_get_element(jsonbSource,
340 sbsrefstate->upperindex,
341 sbsrefstate->numupper,
342 &sbsrefstate->prevnull,
343 false);
344 }
345}
346
347/*
348 * Set up execution state for a jsonb subscript operation. Opposite to the
349 * arrays subscription, there is no limit for number of subscripts as jsonb
350 * type itself doesn't have nesting limits.
351 */
352static void
354 SubscriptingRefState *sbsrefstate,
355 SubscriptExecSteps *methods)
356{
357 JsonbSubWorkspace *workspace;
358 ListCell *lc;
359 int nupper = sbsref->refupperindexpr->length;
360 char *ptr;
361
362 /* Allocate type-specific workspace with space for per-subscript data */
363 workspace = palloc0(MAXALIGN(sizeof(JsonbSubWorkspace)) +
364 nupper * (sizeof(Datum) + sizeof(Oid)));
365 workspace->expectArray = false;
366 ptr = ((char *) workspace) + MAXALIGN(sizeof(JsonbSubWorkspace));
367
368 /*
369 * This coding assumes sizeof(Datum) >= sizeof(Oid), else we might
370 * misalign the indexOid pointer
371 */
372 workspace->index = (Datum *) ptr;
373 ptr += nupper * sizeof(Datum);
374 workspace->indexOid = (Oid *) ptr;
375
376 sbsrefstate->workspace = workspace;
377
378 /* Collect subscript data types necessary at execution time */
379 foreach(lc, sbsref->refupperindexpr)
380 {
381 Node *expr = lfirst(lc);
382 int i = foreach_current_index(lc);
383
384 workspace->indexOid[i] = exprType(expr);
385 }
386
387 /*
388 * Pass back pointers to appropriate step execution functions.
389 */
394}
395
396/*
397 * jsonb_subscript_handler
398 * Subscripting handler for jsonb.
399 *
400 */
401Datum
403{
404 static const SubscriptRoutines sbsroutines = {
406 .exec_setup = jsonb_exec_setup,
407 .fetch_strict = true, /* fetch returns NULL for NULL inputs */
408 .fetch_leakproof = true, /* fetch returns NULL for bad subscript */
409 .store_leakproof = false /* ... but assignment throws error */
410 };
411
412 PG_RETURN_POINTER(&sbsroutines);
413}
Datum idx(PG_FUNCTION_ARGS)
Definition: _int_op.c:259
#define CStringGetTextDatum(s)
Definition: builtins.h:97
#define MAXALIGN(LEN)
Definition: c.h:782
int errhint(const char *fmt,...)
Definition: elog.c:1317
int errcode(int sqlerrcode)
Definition: elog.c:853
int errmsg(const char *fmt,...)
Definition: elog.c:1070
#define ERROR
Definition: elog.h:39
#define ereport(elevel,...)
Definition: elog.h:149
#define DirectFunctionCall1(func, arg1)
Definition: fmgr.h:641
#define PG_RETURN_POINTER(x)
Definition: fmgr.h:361
#define PG_FUNCTION_ARGS
Definition: fmgr.h:193
char * format_type_be(Oid type_oid)
Definition: format_type.c:343
Assert(PointerIsAligned(start, uint64))
Datum int4out(PG_FUNCTION_ARGS)
Definition: int.c:298
int i
Definition: isn.c:74
if(TABLE==NULL||TABLE_index==NULL)
Definition: isn.c:78
@ jbvObject
Definition: jsonb.h:234
@ jbvArray
Definition: jsonb.h:233
@ jbvNull
Definition: jsonb.h:228
static Jsonb * DatumGetJsonbP(Datum d)
Definition: jsonb.h:374
void JsonbToJsonbValue(Jsonb *jsonb, JsonbValue *val)
Definition: jsonb_util.c:72
Jsonb * JsonbValueToJsonb(JsonbValue *val)
Definition: jsonb_util.c:92
static bool jsonb_subscript_check_subscripts(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
Definition: jsonbsubs.c:175
static void jsonb_subscript_transform(SubscriptingRef *sbsref, List *indirection, ParseState *pstate, bool isSlice, bool isAssignment)
Definition: jsonbsubs.c:43
Datum jsonb_subscript_handler(PG_FUNCTION_ARGS)
Definition: jsonbsubs.c:402
static void jsonb_subscript_assign(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
Definition: jsonbsubs.c:261
static void jsonb_subscript_fetch(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
Definition: jsonbsubs.c:235
static void jsonb_exec_setup(const SubscriptingRef *sbsref, SubscriptingRefState *sbsrefstate, SubscriptExecSteps *methods)
Definition: jsonbsubs.c:353
struct JsonbSubWorkspace JsonbSubWorkspace
static void jsonb_subscript_fetch_old(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
Definition: jsonbsubs.c:323
Datum jsonb_set_element(Jsonb *jb, Datum *path, int path_len, JsonbValue *newval)
Definition: jsonfuncs.c:1679
Datum jsonb_get_element(Jsonb *jb, Datum *path, int npath, bool *isnull, bool as_text)
Definition: jsonfuncs.c:1531
List * lappend(List *list, void *datum)
Definition: list.c:339
void * palloc0(Size size)
Definition: mcxt.c:1347
Oid exprType(const Node *expr)
Definition: nodeFuncs.c:42
int exprLocation(const Node *expr)
Definition: nodeFuncs.c:1388
Node * coerce_type(ParseState *pstate, Node *node, Oid inputTypeId, Oid targetTypeId, int32 targetTypeMod, CoercionContext ccontext, CoercionForm cformat, int location)
Definition: parse_coerce.c:157
bool can_coerce_type(int nargs, const Oid *input_typeids, const Oid *target_typeids, CoercionContext ccontext)
Definition: parse_coerce.c:557
Node * transformExpr(ParseState *pstate, Node *expr, ParseExprKind exprKind)
Definition: parse_expr.c:118
int parser_errposition(ParseState *pstate, int location)
Definition: parse_node.c:106
#define lfirst(lc)
Definition: pg_list.h:172
#define lfirst_node(type, lc)
Definition: pg_list.h:176
#define NIL
Definition: pg_list.h:68
#define foreach_current_index(var_or_cell)
Definition: pg_list.h:403
uintptr_t Datum
Definition: postgres.h:69
static char * DatumGetCString(Datum X)
Definition: postgres.h:340
#define InvalidOid
Definition: postgres_ext.h:37
unsigned int Oid
Definition: postgres_ext.h:32
@ COERCE_IMPLICIT_CAST
Definition: primnodes.h:753
@ COERCION_IMPLICIT
Definition: primnodes.h:731
bool is_slice
Definition: parsenodes.h:470
Node * uidx
Definition: parsenodes.h:472
Node * lidx
Definition: parsenodes.h:471
struct SubscriptingRefState * state
Definition: execExpr.h:570
struct ExprEvalStep::@55::@83 sbsref_subscript
struct ExprEvalStep::@55::@84 sbsref
Datum * resvalue
Definition: execExpr.h:310
union ExprEvalStep::@55 d
bool * resnull
Definition: execExpr.h:311
enum jbvType type
Definition: jsonb.h:255
char * val
Definition: jsonb.h:264
Definition: jsonb.h:213
Definition: pg_list.h:54
int length
Definition: pg_list.h:56
Definition: nodes.h:131
ParseExprKind p_expr_kind
Definition: parse_node.h:230
ExecEvalSubroutine sbs_fetch_old
Definition: execExpr.h:817
ExecEvalBoolSubroutine sbs_check_subscripts
Definition: execExpr.h:814
ExecEvalSubroutine sbs_assign
Definition: execExpr.h:816
ExecEvalSubroutine sbs_fetch
Definition: execExpr.h:815
SubscriptTransform transform
Definition: subscripting.h:160
List * refupperindexpr
Definition: primnodes.h:710
List * reflowerindexpr
Definition: primnodes.h:716
Definition: regguts.h:323