PostgreSQL Source Code  git master
jsonbsubs.c File Reference
#include "postgres.h"
#include "executor/execExpr.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "nodes/subscripting.h"
#include "parser/parse_coerce.h"
#include "parser/parse_expr.h"
#include "utils/jsonb.h"
#include "utils/jsonfuncs.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
Include dependency graph for jsonbsubs.c:

Go to the source code of this file.

Data Structures

struct  JsonbSubWorkspace
 

Typedefs

typedef struct JsonbSubWorkspace JsonbSubWorkspace
 

Functions

static void jsonb_subscript_transform (SubscriptingRef *sbsref, List *indirection, ParseState *pstate, bool isSlice, bool isAssignment)
 
static bool jsonb_subscript_check_subscripts (ExprState *state, ExprEvalStep *op, ExprContext *econtext)
 
static void jsonb_subscript_fetch (ExprState *state, ExprEvalStep *op, ExprContext *econtext)
 
static void jsonb_subscript_assign (ExprState *state, ExprEvalStep *op, ExprContext *econtext)
 
static void jsonb_subscript_fetch_old (ExprState *state, ExprEvalStep *op, ExprContext *econtext)
 
static void jsonb_exec_setup (const SubscriptingRef *sbsref, SubscriptingRefState *sbsrefstate, SubscriptExecSteps *methods)
 
Datum jsonb_subscript_handler (PG_FUNCTION_ARGS)
 

Typedef Documentation

◆ JsonbSubWorkspace

Function Documentation

◆ jsonb_exec_setup()

static void jsonb_exec_setup ( const SubscriptingRef sbsref,
SubscriptingRefState sbsrefstate,
SubscriptExecSteps methods 
)
static

Definition at line 356 of file jsonbsubs.c.

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 }
#define MAXALIGN(LEN)
Definition: c.h:800
int i
Definition: isn.c:73
static bool jsonb_subscript_check_subscripts(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
Definition: jsonbsubs.c:178
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_subscript_fetch_old(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
Definition: jsonbsubs.c:326
void * palloc0(Size size)
Definition: mcxt.c:1257
Oid exprType(const Node *expr)
Definition: nodeFuncs.c:43
#define lfirst(lc)
Definition: pg_list.h:172
#define foreach_current_index(cell)
Definition: pg_list.h:403
uintptr_t Datum
Definition: postgres.h:64
unsigned int Oid
Definition: postgres_ext.h:31
int length
Definition: pg_list.h:56
Definition: nodes.h:129
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
List * refupperindexpr
Definition: primnodes.h:620

References JsonbSubWorkspace::expectArray, exprType(), foreach_current_index, i, JsonbSubWorkspace::index, JsonbSubWorkspace::indexOid, jsonb_subscript_assign(), jsonb_subscript_check_subscripts(), jsonb_subscript_fetch(), jsonb_subscript_fetch_old(), List::length, lfirst, MAXALIGN, palloc0(), SubscriptingRef::refupperindexpr, SubscriptExecSteps::sbs_assign, SubscriptExecSteps::sbs_check_subscripts, SubscriptExecSteps::sbs_fetch, SubscriptExecSteps::sbs_fetch_old, and SubscriptingRefState::workspace.

Referenced by jsonb_subscript_handler().

◆ jsonb_subscript_assign()

static void jsonb_subscript_assign ( ExprState state,
ExprEvalStep op,
ExprContext econtext 
)
static

Definition at line 264 of file jsonbsubs.c.

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 }
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
Datum jsonb_set_element(Jsonb *jb, Datum *path, int path_len, JsonbValue *newval)
Definition: jsonfuncs.c:1668
union ExprEvalStep::@51 d
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

References ExprEvalStep::d, DatumGetJsonbP(), JsonbSubWorkspace::expectArray, if(), JsonbSubWorkspace::index, jbvArray, jbvNull, jbvObject, jsonb_set_element(), JsonbToJsonbValue(), JsonbValueToJsonb(), SubscriptingRefState::numupper, SubscriptingRefState::replacenull, SubscriptingRefState::replacevalue, ExprEvalStep::resnull, ExprEvalStep::resvalue, ExprEvalStep::sbsref, JsonbValue::type, JsonbValue::val, and SubscriptingRefState::workspace.

Referenced by jsonb_exec_setup().

◆ jsonb_subscript_check_subscripts()

static bool jsonb_subscript_check_subscripts ( ExprState state,
ExprEvalStep op,
ExprContext econtext 
)
static

Definition at line 178 of file jsonbsubs.c.

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 }
#define CStringGetTextDatum(s)
Definition: builtins.h:94
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
Datum int4out(PG_FUNCTION_ARGS)
Definition: int.c:298
static char * DatumGetCString(Datum X)
Definition: postgres.h:335
struct ExprEvalStep::@51::@78 sbsref_subscript

References CStringGetTextDatum, ExprEvalStep::d, DatumGetCString(), DirectFunctionCall1, ereport, errcode(), errmsg(), ERROR, JsonbSubWorkspace::expectArray, i, if(), JsonbSubWorkspace::index, JsonbSubWorkspace::indexOid, int4out(), SubscriptingRefState::isassignment, SubscriptingRefState::numupper, ExprEvalStep::resnull, ExprEvalStep::sbsref_subscript, SubscriptingRefState::upperindex, SubscriptingRefState::upperindexnull, SubscriptingRefState::upperprovided, and SubscriptingRefState::workspace.

Referenced by jsonb_exec_setup().

◆ jsonb_subscript_fetch()

static void jsonb_subscript_fetch ( ExprState state,
ExprEvalStep op,
ExprContext econtext 
)
static

Definition at line 238 of file jsonbsubs.c.

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 }
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')

References Assert(), ExprEvalStep::d, DatumGetJsonbP(), JsonbSubWorkspace::index, jsonb_get_element(), SubscriptingRefState::numupper, ExprEvalStep::resnull, ExprEvalStep::resvalue, ExprEvalStep::sbsref, and SubscriptingRefState::workspace.

Referenced by jsonb_exec_setup().

◆ jsonb_subscript_fetch_old()

static void jsonb_subscript_fetch_old ( ExprState state,
ExprEvalStep op,
ExprContext econtext 
)
static

Definition at line 326 of file jsonbsubs.c.

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 }

References ExprEvalStep::d, DatumGetJsonbP(), jsonb_get_element(), SubscriptingRefState::numupper, SubscriptingRefState::prevnull, SubscriptingRefState::prevvalue, ExprEvalStep::resnull, ExprEvalStep::resvalue, ExprEvalStep::sbsref, and SubscriptingRefState::upperindex.

Referenced by jsonb_exec_setup().

◆ jsonb_subscript_handler()

Datum jsonb_subscript_handler ( PG_FUNCTION_ARGS  )

Definition at line 405 of file jsonbsubs.c.

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 }
#define PG_RETURN_POINTER(x)
Definition: fmgr.h:361
static void jsonb_subscript_transform(SubscriptingRef *sbsref, List *indirection, ParseState *pstate, bool isSlice, bool isAssignment)
Definition: jsonbsubs.c:46
static void jsonb_exec_setup(const SubscriptingRef *sbsref, SubscriptingRefState *sbsrefstate, SubscriptExecSteps *methods)
Definition: jsonbsubs.c:356
SubscriptTransform transform
Definition: subscripting.h:160

References jsonb_exec_setup(), jsonb_subscript_transform(), PG_RETURN_POINTER, and SubscriptRoutines::transform.

◆ jsonb_subscript_transform()

static void jsonb_subscript_transform ( SubscriptingRef sbsref,
List indirection,
ParseState pstate,
bool  isSlice,
bool  isAssignment 
)
static

Definition at line 46 of file jsonbsubs.c.

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 }
Datum idx(PG_FUNCTION_ARGS)
Definition: _int_op.c:259
int errhint(const char *fmt,...)
Definition: elog.c:1316
char * format_type_be(Oid type_oid)
Definition: format_type.c:343
List * lappend(List *list, void *datum)
Definition: list.c:338
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_node(type, lc)
Definition: pg_list.h:176
#define NIL
Definition: pg_list.h:68
#define InvalidOid
Definition: postgres_ext.h:36
@ COERCE_IMPLICIT_CAST
Definition: primnodes.h:663
@ COERCION_IMPLICIT
Definition: primnodes.h:641
bool is_slice
Definition: parsenodes.h:456
Node * uidx
Definition: parsenodes.h:458
Node * lidx
Definition: parsenodes.h:457
Definition: pg_list.h:54
ParseExprKind p_expr_kind
Definition: parse_node.h:210
List * reflowerindexpr
Definition: primnodes.h:626

References Assert(), can_coerce_type(), COERCE_IMPLICIT_CAST, coerce_type(), COERCION_IMPLICIT, ereport, errcode(), errhint(), errmsg(), ERROR, exprLocation(), exprType(), format_type_be(), i, idx(), InvalidOid, A_Indices::is_slice, lappend(), lfirst_node, A_Indices::lidx, NIL, ParseState::p_expr_kind, parser_errposition(), SubscriptingRef::reflowerindexpr, SubscriptingRef::refupperindexpr, transformExpr(), and A_Indices::uidx.

Referenced by jsonb_subscript_handler().