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-2024, 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 */
27 typedef 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  */
42 static void
44  List *indirection,
45  ParseState *pstate,
46  bool isSlice,
47  bool isAssignment)
48 {
49  List *upperIndexpr = NIL;
50  ListCell *idx;
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 
65  ereport(ERROR,
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)
98  ereport(ERROR,
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)
112  ereport(ERROR,
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)
134  ereport(ERROR,
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);
146  ereport(ERROR,
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  */
174 static 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)
202  ereport(ERROR,
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  */
234 static 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  */
260 static 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  */
322 static 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  */
352 static 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  */
391  methods->sbs_fetch = jsonb_subscript_fetch;
394 }
395 
396 /*
397  * jsonb_subscript_handler
398  * Subscripting handler for jsonb.
399  *
400  */
401 Datum
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:811
#define Assert(condition)
Definition: c.h:858
int errhint(const char *fmt,...)
Definition: elog.c:1319
int errcode(int sqlerrcode)
Definition: elog.c:859
int errmsg(const char *fmt,...)
Definition: elog.c:1072
#define ERROR
Definition: elog.h:39
#define ereport(elevel,...)
Definition: elog.h:149
#define DirectFunctionCall1(func, arg1)
Definition: fmgr.h:642
#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
Datum int4out(PG_FUNCTION_ARGS)
Definition: int.c:298
int i
Definition: isn.c:73
if(TABLE==NULL||TABLE_index==NULL)
Definition: isn.c:77
@ jbvObject
Definition: jsonb.h:234
@ jbvArray
Definition: jsonb.h:233
@ jbvNull
Definition: jsonb.h:228
static Jsonb * DatumGetJsonbP(Datum d)
Definition: jsonb.h:374
Jsonb * JsonbValueToJsonb(JsonbValue *val)
Definition: jsonb_util.c:92
void JsonbToJsonbValue(Jsonb *jsonb, JsonbValue *val)
Definition: jsonb_util.c:72
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:1676
Datum jsonb_get_element(Jsonb *jb, Datum *path, int npath, bool *isnull, bool as_text)
Definition: jsonfuncs.c:1528
List * lappend(List *list, void *datum)
Definition: list.c:339
void * palloc0(Size size)
Definition: mcxt.c:1346
Oid exprType(const Node *expr)
Definition: nodeFuncs.c:42
int exprLocation(const Node *expr)
Definition: nodeFuncs.c:1386
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:556
Node * transformExpr(ParseState *pstate, Node *expr, ParseExprKind exprKind)
Definition: parse_expr.c:121
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
static char * DatumGetCString(Datum X)
Definition: postgres.h:335
uintptr_t Datum
Definition: postgres.h:64
#define InvalidOid
Definition: postgres_ext.h:36
unsigned int Oid
Definition: postgres_ext.h:31
@ COERCE_IMPLICIT_CAST
Definition: primnodes.h:706
@ COERCION_IMPLICIT
Definition: primnodes.h:684
bool is_slice
Definition: parsenodes.h:459
Node * uidx
Definition: parsenodes.h:461
Node * lidx
Definition: parsenodes.h:460
union ExprEvalStep::@54 d
Datum * resvalue
Definition: execExpr.h:284
struct ExprEvalStep::@54::@82 sbsref
bool * resnull
Definition: execExpr.h:285
struct ExprEvalStep::@54::@81 sbsref_subscript
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:129
ParseExprKind p_expr_kind
Definition: parse_node.h:211
ExecEvalSubroutine sbs_fetch_old
Definition: execExpr.h:758
ExecEvalBoolSubroutine sbs_check_subscripts
Definition: execExpr.h:755
ExecEvalSubroutine sbs_assign
Definition: execExpr.h:757
ExecEvalSubroutine sbs_fetch
Definition: execExpr.h:756
SubscriptTransform transform
Definition: subscripting.h:160
List * refupperindexpr
Definition: primnodes.h:663
List * reflowerindexpr
Definition: primnodes.h:669
Definition: regguts.h:323