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 357 of file jsonbsubs.c.

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().

360 {
361  JsonbSubWorkspace *workspace;
362  ListCell *lc;
363  int nupper = sbsref->refupperindexpr->length;
364  char *ptr;
365 
366  /* Allocate type-specific workspace with space for per-subscript data */
367  workspace = palloc0(MAXALIGN(sizeof(JsonbSubWorkspace)) +
368  nupper * (sizeof(Datum) + sizeof(Oid)));
369  workspace->expectArray = false;
370  ptr = ((char *) workspace) + MAXALIGN(sizeof(JsonbSubWorkspace));
371 
372  /*
373  * This coding assumes sizeof(Datum) >= sizeof(Oid), else we might
374  * misalign the indexOid pointer
375  */
376  workspace->index = (Datum *) ptr;
377  ptr += nupper * sizeof(Datum);
378  workspace->indexOid = (Oid *) ptr;
379 
380  sbsrefstate->workspace = workspace;
381 
382  /* Collect subscript data types necessary at execution time */
383  foreach(lc, sbsref->refupperindexpr)
384  {
385  Node *expr = lfirst(lc);
386  int i = foreach_current_index(lc);
387 
388  workspace->indexOid[i] = exprType(expr);
389  }
390 
391  /*
392  * Pass back pointers to appropriate step execution functions.
393  */
395  methods->sbs_fetch = jsonb_subscript_fetch;
398 }
ExecEvalSubroutine sbs_fetch
Definition: execExpr.h:709
Definition: nodes.h:539
ExecEvalSubroutine sbs_fetch_old
Definition: execExpr.h:711
ExecEvalSubroutine sbs_assign
Definition: execExpr.h:710
unsigned int Oid
Definition: postgres_ext.h:31
List * refupperindexpr
Definition: primnodes.h:444
static void jsonb_subscript_assign(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
Definition: jsonbsubs.c:265
static bool jsonb_subscript_check_subscripts(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
Definition: jsonbsubs.c:179
void * palloc0(Size size)
Definition: mcxt.c:1093
uintptr_t Datum
Definition: postgres.h:411
#define lfirst(lc)
Definition: pg_list.h:169
static void jsonb_subscript_fetch(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
Definition: jsonbsubs.c:239
Oid exprType(const Node *expr)
Definition: nodeFuncs.c:41
#define MAXALIGN(LEN)
Definition: c.h:757
int length
Definition: pg_list.h:53
int i
ExecEvalBoolSubroutine sbs_check_subscripts
Definition: execExpr.h:708
static void jsonb_subscript_fetch_old(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
Definition: jsonbsubs.c:327
#define foreach_current_index(cell)
Definition: pg_list.h:382

◆ jsonb_subscript_assign()

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

Definition at line 265 of file jsonbsubs.c.

References ExprEvalStep::d, DatumGetJsonbP, JsonbSubWorkspace::expectArray, 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().

268 {
269  SubscriptingRefState *sbsrefstate = op->d.sbsref.state;
270  JsonbSubWorkspace *workspace = (JsonbSubWorkspace *) sbsrefstate->workspace;
271  Jsonb *jsonbSource;
272  JsonbValue replacevalue;
273 
274  if (sbsrefstate->replacenull)
275  replacevalue.type = jbvNull;
276  else
278  &replacevalue);
279 
280  /*
281  * In case if the input container is null, set up an empty jsonb and
282  * proceed with the assignment.
283  */
284  if (*op->resnull)
285  {
286  JsonbValue newSource;
287 
288  /*
289  * To avoid any surprising results, set up an empty jsonb array in
290  * case of an array is expected (i.e. the first subscript is integer),
291  * otherwise jsonb object.
292  */
293  if (workspace->expectArray)
294  {
295  newSource.type = jbvArray;
296  newSource.val.array.nElems = 0;
297  newSource.val.array.rawScalar = false;
298  }
299  else
300  {
301  newSource.type = jbvObject;
302  newSource.val.object.nPairs = 0;
303  }
304 
305  jsonbSource = JsonbValueToJsonb(&newSource);
306  *op->resnull = false;
307  }
308  else
309  jsonbSource = DatumGetJsonbP(*op->resvalue);
310 
311  *op->resvalue = jsonb_set_element(jsonbSource,
312  workspace->index,
313  sbsrefstate->numupper,
314  &replacevalue);
315  /* The result is never NULL, so no need to change *op->resnull */
316 }
void JsonbToJsonbValue(Jsonb *jsonb, JsonbValue *val)
Definition: jsonb_util.c:72
Jsonb * JsonbValueToJsonb(JsonbValue *val)
Definition: jsonb_util.c:92
Definition: jsonb.h:220
Datum * resvalue
Definition: execExpr.h:273
#define DatumGetJsonbP(d)
Definition: jsonb.h:71
char * val
Definition: jsonb.h:272
bool * resnull
Definition: execExpr.h:274
Definition: jsonb.h:236
Datum jsonb_set_element(Jsonb *jb, Datum *path, int path_len, JsonbValue *newval)
Definition: jsonfuncs.c:1615
union ExprEvalStep::@49 d
struct ExprEvalStep::@49::@77 sbsref
enum jbvType type
Definition: jsonb.h:263

◆ jsonb_subscript_check_subscripts()

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

Definition at line 179 of file jsonbsubs.c.

References CStringGetTextDatum, ExprEvalStep::d, DatumGetCString, DirectFunctionCall1, ereport, errcode(), errmsg(), ERROR, JsonbSubWorkspace::expectArray, i, 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().

182 {
183  SubscriptingRefState *sbsrefstate = op->d.sbsref_subscript.state;
184  JsonbSubWorkspace *workspace = (JsonbSubWorkspace *) sbsrefstate->workspace;
185 
186  /*
187  * In case if the first subscript is an integer, the source jsonb is
188  * expected to be an array. This information is not used directly, all
189  * such cases are handled within corresponding jsonb assign functions. But
190  * if the source jsonb is NULL the expected type will be used to construct
191  * an empty source.
192  */
193  if (sbsrefstate->numupper > 0 && sbsrefstate->upperprovided[0] &&
194  !sbsrefstate->upperindexnull[0] && workspace->indexOid[0] == INT4OID)
195  workspace->expectArray = true;
196 
197  /* Process upper subscripts */
198  for (int i = 0; i < sbsrefstate->numupper; i++)
199  {
200  if (sbsrefstate->upperprovided[i])
201  {
202  /* If any index expr yields NULL, result is NULL or error */
203  if (sbsrefstate->upperindexnull[i])
204  {
205  if (sbsrefstate->isassignment)
206  ereport(ERROR,
207  (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
208  errmsg("jsonb subscript in assignment must not be null")));
209  *op->resnull = true;
210  return false;
211  }
212 
213  /*
214  * For jsonb fetch and assign functions we need to provide path in
215  * text format. Convert if it's not already text.
216  */
217  if (workspace->indexOid[i] == INT4OID)
218  {
219  Datum datum = sbsrefstate->upperindex[i];
220  char *cs = DatumGetCString(DirectFunctionCall1(int4out, datum));
221 
222  workspace->index[i] = CStringGetTextDatum(cs);
223  }
224  else
225  workspace->index[i] = sbsrefstate->upperindex[i];
226  }
227  }
228 
229  return true;
230 }
bool * resnull
Definition: execExpr.h:274
int errcode(int sqlerrcode)
Definition: elog.c:698
#define DirectFunctionCall1(func, arg1)
Definition: fmgr.h:626
#define ERROR
Definition: elog.h:46
#define DatumGetCString(X)
Definition: postgres.h:610
union ExprEvalStep::@49 d
Datum int4out(PG_FUNCTION_ARGS)
Definition: int.c:277
struct ExprEvalStep::@49::@76 sbsref_subscript
uintptr_t Datum
Definition: postgres.h:411
#define ereport(elevel,...)
Definition: elog.h:157
int errmsg(const char *fmt,...)
Definition: elog.c:909
int i
#define CStringGetTextDatum(s)
Definition: builtins.h:82

◆ jsonb_subscript_fetch()

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

Definition at line 239 of file jsonbsubs.c.

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().

242 {
243  SubscriptingRefState *sbsrefstate = op->d.sbsref.state;
244  JsonbSubWorkspace *workspace = (JsonbSubWorkspace *) sbsrefstate->workspace;
245  Jsonb *jsonbSource;
246 
247  /* Should not get here if source jsonb (or any subscript) is null */
248  Assert(!(*op->resnull));
249 
250  jsonbSource = DatumGetJsonbP(*op->resvalue);
251  *op->resvalue = jsonb_get_element(jsonbSource,
252  workspace->index,
253  sbsrefstate->numupper,
254  op->resnull,
255  false);
256 }
Definition: jsonb.h:220
Datum * resvalue
Definition: execExpr.h:273
#define DatumGetJsonbP(d)
Definition: jsonb.h:71
bool * resnull
Definition: execExpr.h:274
union ExprEvalStep::@49 d
Datum jsonb_get_element(Jsonb *jb, Datum *path, int npath, bool *isnull, bool as_text)
Definition: jsonfuncs.c:1469
#define Assert(condition)
Definition: c.h:804
struct ExprEvalStep::@49::@77 sbsref

◆ jsonb_subscript_fetch_old()

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

Definition at line 327 of file jsonbsubs.c.

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().

330 {
331  SubscriptingRefState *sbsrefstate = op->d.sbsref.state;
332 
333  if (*op->resnull)
334  {
335  /* whole jsonb is null, so any element is too */
336  sbsrefstate->prevvalue = (Datum) 0;
337  sbsrefstate->prevnull = true;
338  }
339  else
340  {
341  Jsonb *jsonbSource = DatumGetJsonbP(*op->resvalue);
342 
343  sbsrefstate->prevvalue = jsonb_get_element(jsonbSource,
344  sbsrefstate->upperindex,
345  sbsrefstate->numupper,
346  &sbsrefstate->prevnull,
347  false);
348  }
349 }
Definition: jsonb.h:220
Datum * resvalue
Definition: execExpr.h:273
#define DatumGetJsonbP(d)
Definition: jsonb.h:71
bool * resnull
Definition: execExpr.h:274
union ExprEvalStep::@49 d
uintptr_t Datum
Definition: postgres.h:411
Datum jsonb_get_element(Jsonb *jb, Datum *path, int npath, bool *isnull, bool as_text)
Definition: jsonfuncs.c:1469
struct ExprEvalStep::@49::@77 sbsref

◆ jsonb_subscript_handler()

Datum jsonb_subscript_handler ( PG_FUNCTION_ARGS  )

Definition at line 406 of file jsonbsubs.c.

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

407 {
408  static const SubscriptRoutines sbsroutines = {
410  .exec_setup = jsonb_exec_setup,
411  .fetch_strict = true, /* fetch returns NULL for NULL inputs */
412  .fetch_leakproof = true, /* fetch returns NULL for bad subscript */
413  .store_leakproof = false /* ... but assignment throws error */
414  };
415 
416  PG_RETURN_POINTER(&sbsroutines);
417 }
#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
SubscriptTransform transform
Definition: subscripting.h:160
static void jsonb_exec_setup(const SubscriptingRef *sbsref, SubscriptingRefState *sbsrefstate, SubscriptExecSteps *methods)
Definition: jsonbsubs.c:357

◆ 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.

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

Referenced by jsonb_subscript_handler().

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  {
61  A_Indices *ai = lfirst_node(A_Indices, idx);
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 is not supported"),
104  errhint("Jsonb subscript must be coerced "
105  "only to one type, integer or text."),
106  parser_errposition(pstate, exprLocation(subExpr))));
107 
108  targetType = targets[i];
109  }
110  }
111 
112  /*
113  * No suitable types were found, failure.
114  */
115  if (targetType == UNKNOWNOID)
116  ereport(ERROR,
117  (errcode(ERRCODE_DATATYPE_MISMATCH),
118  errmsg("subscript type is not supported"),
119  errhint("Jsonb subscript must be coerced to either integer or text"),
120  parser_errposition(pstate, exprLocation(subExpr))));
121  }
122  else
123  targetType = TEXTOID;
124 
125  /*
126  * We known from can_coerce_type that coercion will succeed, so
127  * coerce_type could be used. Note the implicit coercion context,
128  * which is required to handle subscripts of different types,
129  * similar to overloaded functions.
130  */
131  subExpr = coerce_type(pstate,
132  subExpr, subExprType,
133  targetType, -1,
136  -1);
137  if (subExpr == NULL)
138  ereport(ERROR,
139  (errcode(ERRCODE_DATATYPE_MISMATCH),
140  errmsg("jsonb subscript must have text type"),
141  parser_errposition(pstate, exprLocation(subExpr))));
142  }
143  else
144  {
145  /*
146  * Slice with omitted upper bound. Should not happen as we already
147  * errored out on slice earlier, but handle this just in case.
148  */
149  Assert(isSlice && ai->is_slice);
150  ereport(ERROR,
151  (errcode(ERRCODE_DATATYPE_MISMATCH),
152  errmsg("jsonb subscript does not support slices"),
153  parser_errposition(pstate, exprLocation(ai->uidx))));
154  }
155 
156  upperIndexpr = lappend(upperIndexpr, subExpr);
157  }
158 
159  /* store the transformed lists into the SubscriptRef node */
160  sbsref->refupperindexpr = upperIndexpr;
161  sbsref->reflowerindexpr = NIL;
162 
163  /* Determine the result type of the subscripting operation; always jsonb */
164  sbsref->refrestype = JSONBOID;
165  sbsref->reftypmod = -1;
166 }
#define NIL
Definition: pg_list.h:65
int errhint(const char *fmt,...)
Definition: elog.c:1156
int exprLocation(const Node *expr)
Definition: nodeFuncs.c:1250
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:94
Node * coerce_type(ParseState *pstate, Node *node, Oid inputTypeId, Oid targetTypeId, int32 targetTypeMod, CoercionContext ccontext, CoercionForm cformat, int location)
Definition: parse_coerce.c:157
Definition: nodes.h:539
int errcode(int sqlerrcode)
Definition: elog.c:698
Datum idx(PG_FUNCTION_ARGS)
Definition: _int_op.c:259
unsigned int Oid
Definition: postgres_ext.h:31
List * refupperindexpr
Definition: primnodes.h:444
bool is_slice
Definition: parsenodes.h:394
#define ERROR
Definition: elog.h:46
#define lfirst_node(type, lc)
Definition: pg_list.h:172
List * lappend(List *list, void *datum)
Definition: list.c:336
ParseExprKind p_expr_kind
Definition: parse_node.h:196
#define InvalidOid
Definition: postgres_ext.h:36
#define ereport(elevel,...)
Definition: elog.h:157
#define Assert(condition)
Definition: c.h:804
Oid exprType(const Node *expr)
Definition: nodeFuncs.c:41
int parser_errposition(ParseState *pstate, int location)
Definition: parse_node.c:111
Node * lidx
Definition: parsenodes.h:395
int errmsg(const char *fmt,...)
Definition: elog.c:909
List * reflowerindexpr
Definition: primnodes.h:446
int i
Node * uidx
Definition: parsenodes.h:396
Definition: pg_list.h:50