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