Coverage Report

Created: 2025-07-03 06:49

/src/postgres/src/backend/utils/adt/jsonpath_exec.c
Line
Count
Source (jump to first uncovered line)
1
/*-------------------------------------------------------------------------
2
 *
3
 * jsonpath_exec.c
4
 *   Routines for SQL/JSON path execution.
5
 *
6
 * Jsonpath is executed in the global context stored in JsonPathExecContext,
7
 * which is passed to almost every function involved into execution.  Entry
8
 * point for jsonpath execution is executeJsonPath() function, which
9
 * initializes execution context including initial JsonPathItem and JsonbValue,
10
 * flags, stack for calculation of @ in filters.
11
 *
12
 * The result of jsonpath query execution is enum JsonPathExecResult and
13
 * if succeeded sequence of JsonbValue, written to JsonValueList *found, which
14
 * is passed through the jsonpath items.  When found == NULL, we're inside
15
 * exists-query and we're interested only in whether result is empty.  In this
16
 * case execution is stopped once first result item is found, and the only
17
 * execution result is JsonPathExecResult.  The values of JsonPathExecResult
18
 * are following:
19
 * - jperOk     -- result sequence is not empty
20
 * - jperNotFound -- result sequence is empty
21
 * - jperError    -- error occurred during execution
22
 *
23
 * Jsonpath is executed recursively (see executeItem()) starting form the
24
 * first path item (which in turn might be, for instance, an arithmetic
25
 * expression evaluated separately).  On each step single JsonbValue obtained
26
 * from previous path item is processed.  The result of processing is a
27
 * sequence of JsonbValue (probably empty), which is passed to the next path
28
 * item one by one.  When there is no next path item, then JsonbValue is added
29
 * to the 'found' list.  When found == NULL, then execution functions just
30
 * return jperOk (see executeNextItem()).
31
 *
32
 * Many of jsonpath operations require automatic unwrapping of arrays in lax
33
 * mode.  So, if input value is array, then corresponding operation is
34
 * processed not on array itself, but on all of its members one by one.
35
 * executeItemOptUnwrapTarget() function have 'unwrap' argument, which indicates
36
 * whether unwrapping of array is needed.  When unwrap == true, each of array
37
 * members is passed to executeItemOptUnwrapTarget() again but with unwrap == false
38
 * in order to avoid subsequent array unwrapping.
39
 *
40
 * All boolean expressions (predicates) are evaluated by executeBoolItem()
41
 * function, which returns tri-state JsonPathBool.  When error is occurred
42
 * during predicate execution, it returns jpbUnknown.  According to standard
43
 * predicates can be only inside filters.  But we support their usage as
44
 * jsonpath expression.  This helps us to implement @@ operator.  In this case
45
 * resulting JsonPathBool is transformed into jsonb bool or null.
46
 *
47
 * Arithmetic and boolean expression are evaluated recursively from expression
48
 * tree top down to the leaves.  Therefore, for binary arithmetic expressions
49
 * we calculate operands first.  Then we check that results are numeric
50
 * singleton lists, calculate the result and pass it to the next path item.
51
 *
52
 * Copyright (c) 2019-2025, PostgreSQL Global Development Group
53
 *
54
 * IDENTIFICATION
55
 *  src/backend/utils/adt/jsonpath_exec.c
56
 *
57
 *-------------------------------------------------------------------------
58
 */
59
60
#include "postgres.h"
61
62
#include "catalog/pg_collation.h"
63
#include "catalog/pg_type.h"
64
#include "funcapi.h"
65
#include "miscadmin.h"
66
#include "nodes/miscnodes.h"
67
#include "nodes/nodeFuncs.h"
68
#include "regex/regex.h"
69
#include "utils/builtins.h"
70
#include "utils/date.h"
71
#include "utils/datetime.h"
72
#include "utils/float.h"
73
#include "utils/formatting.h"
74
#include "utils/json.h"
75
#include "utils/jsonpath.h"
76
#include "utils/memutils.h"
77
#include "utils/timestamp.h"
78
79
/*
80
 * Represents "base object" and its "id" for .keyvalue() evaluation.
81
 */
82
typedef struct JsonBaseObjectInfo
83
{
84
  JsonbContainer *jbc;
85
  int     id;
86
} JsonBaseObjectInfo;
87
88
/* Callbacks for executeJsonPath() */
89
typedef JsonbValue *(*JsonPathGetVarCallback) (void *vars, char *varName, int varNameLen,
90
                         JsonbValue *baseObject, int *baseObjectId);
91
typedef int (*JsonPathCountVarsCallback) (void *vars);
92
93
/*
94
 * Context of jsonpath execution.
95
 */
96
typedef struct JsonPathExecContext
97
{
98
  void     *vars;     /* variables to substitute into jsonpath */
99
  JsonPathGetVarCallback getVar;  /* callback to extract a given variable
100
                   * from 'vars' */
101
  JsonbValue *root;     /* for $ evaluation */
102
  JsonbValue *current;    /* for @ evaluation */
103
  JsonBaseObjectInfo baseObject;  /* "base object" for .keyvalue()
104
                   * evaluation */
105
  int     lastGeneratedObjectId;  /* "id" counter for .keyvalue()
106
                     * evaluation */
107
  int     innermostArraySize; /* for LAST array index evaluation */
108
  bool    laxMode;    /* true for "lax" mode, false for "strict"
109
                 * mode */
110
  bool    ignoreStructuralErrors; /* with "true" structural errors such
111
                     * as absence of required json item or
112
                     * unexpected json item type are
113
                     * ignored */
114
  bool    throwErrors;  /* with "false" all suppressible errors are
115
                 * suppressed */
116
  bool    useTz;
117
} JsonPathExecContext;
118
119
/* Context for LIKE_REGEX execution. */
120
typedef struct JsonLikeRegexContext
121
{
122
  text     *regex;
123
  int     cflags;
124
} JsonLikeRegexContext;
125
126
/* Result of jsonpath predicate evaluation */
127
typedef enum JsonPathBool
128
{
129
  jpbFalse = 0,
130
  jpbTrue = 1,
131
  jpbUnknown = 2
132
} JsonPathBool;
133
134
/* Result of jsonpath expression evaluation */
135
typedef enum JsonPathExecResult
136
{
137
  jperOk = 0,
138
  jperNotFound = 1,
139
  jperError = 2
140
} JsonPathExecResult;
141
142
0
#define jperIsError(jper)     ((jper) == jperError)
143
144
/*
145
 * List of jsonb values with shortcut for single-value list.
146
 */
147
typedef struct JsonValueList
148
{
149
  JsonbValue *singleton;
150
  List     *list;
151
} JsonValueList;
152
153
typedef struct JsonValueListIterator
154
{
155
  JsonbValue *value;
156
  List     *list;
157
  ListCell   *next;
158
} JsonValueListIterator;
159
160
/* Structures for JSON_TABLE execution  */
161
162
/*
163
 * Struct holding the result of jsonpath evaluation, to be used as source row
164
 * for JsonTableGetValue() which in turn computes the values of individual
165
 * JSON_TABLE columns.
166
 */
167
typedef struct JsonTablePlanRowSource
168
{
169
  Datum   value;
170
  bool    isnull;
171
} JsonTablePlanRowSource;
172
173
/*
174
 * State of evaluation of row pattern derived by applying jsonpath given in
175
 * a JsonTablePlan to an input document given in the parent TableFunc.
176
 */
177
typedef struct JsonTablePlanState
178
{
179
  /* Original plan */
180
  JsonTablePlan *plan;
181
182
  /* The following fields are only valid for JsonTablePathScan plans */
183
184
  /* jsonpath to evaluate against the input doc to get the row pattern */
185
  JsonPath   *path;
186
187
  /*
188
   * Memory context to use when evaluating the row pattern from the jsonpath
189
   */
190
  MemoryContext mcxt;
191
192
  /* PASSING arguments passed to jsonpath executor */
193
  List     *args;
194
195
  /* List and iterator of jsonpath result values */
196
  JsonValueList found;
197
  JsonValueListIterator iter;
198
199
  /* Currently selected row for JsonTableGetValue() to use */
200
  JsonTablePlanRowSource current;
201
202
  /* Counter for ORDINAL columns */
203
  int     ordinal;
204
205
  /* Nested plan, if any */
206
  struct JsonTablePlanState *nested;
207
208
  /* Left sibling, if any */
209
  struct JsonTablePlanState *left;
210
211
  /* Right sibling, if any */
212
  struct JsonTablePlanState *right;
213
214
  /* Parent plan, if this is a nested plan */
215
  struct JsonTablePlanState *parent;
216
} JsonTablePlanState;
217
218
/* Random number to identify JsonTableExecContext for sanity checking */
219
0
#define JSON_TABLE_EXEC_CONTEXT_MAGIC   418352867
220
221
typedef struct JsonTableExecContext
222
{
223
  int     magic;
224
225
  /* State of the plan providing a row evaluated from "root" jsonpath */
226
  JsonTablePlanState *rootplanstate;
227
228
  /*
229
   * Per-column JsonTablePlanStates for all columns including the nested
230
   * ones.
231
   */
232
  JsonTablePlanState **colplanstates;
233
} JsonTableExecContext;
234
235
/* strict/lax flags is decomposed into four [un]wrap/error flags */
236
0
#define jspStrictAbsenceOfErrors(cxt) (!(cxt)->laxMode)
237
0
#define jspAutoUnwrap(cxt)        ((cxt)->laxMode)
238
0
#define jspAutoWrap(cxt)        ((cxt)->laxMode)
239
0
#define jspIgnoreStructuralErrors(cxt)  ((cxt)->ignoreStructuralErrors)
240
0
#define jspThrowErrors(cxt)       ((cxt)->throwErrors)
241
242
/* Convenience macro: return or throw error depending on context */
243
0
#define RETURN_ERROR(throw_error) \
244
0
do { \
245
0
  if (jspThrowErrors(cxt)) \
246
0
    throw_error; \
247
0
  else \
248
0
    return jperError; \
249
0
} while (0)
250
251
typedef JsonPathBool (*JsonPathPredicateCallback) (JsonPathItem *jsp,
252
                           JsonbValue *larg,
253
                           JsonbValue *rarg,
254
                           void *param);
255
typedef Numeric (*BinaryArithmFunc) (Numeric num1, Numeric num2, bool *error);
256
257
static JsonPathExecResult executeJsonPath(JsonPath *path, void *vars,
258
                      JsonPathGetVarCallback getVar,
259
                      JsonPathCountVarsCallback countVars,
260
                      Jsonb *json, bool throwErrors,
261
                      JsonValueList *result, bool useTz);
262
static JsonPathExecResult executeItem(JsonPathExecContext *cxt,
263
                    JsonPathItem *jsp, JsonbValue *jb, JsonValueList *found);
264
static JsonPathExecResult executeItemOptUnwrapTarget(JsonPathExecContext *cxt,
265
                           JsonPathItem *jsp, JsonbValue *jb,
266
                           JsonValueList *found, bool unwrap);
267
static JsonPathExecResult executeItemUnwrapTargetArray(JsonPathExecContext *cxt,
268
                             JsonPathItem *jsp, JsonbValue *jb,
269
                             JsonValueList *found, bool unwrapElements);
270
static JsonPathExecResult executeNextItem(JsonPathExecContext *cxt,
271
                      JsonPathItem *cur, JsonPathItem *next,
272
                      JsonbValue *v, JsonValueList *found, bool copy);
273
static JsonPathExecResult executeItemOptUnwrapResult(JsonPathExecContext *cxt, JsonPathItem *jsp, JsonbValue *jb,
274
                           bool unwrap, JsonValueList *found);
275
static JsonPathExecResult executeItemOptUnwrapResultNoThrow(JsonPathExecContext *cxt, JsonPathItem *jsp,
276
                              JsonbValue *jb, bool unwrap, JsonValueList *found);
277
static JsonPathBool executeBoolItem(JsonPathExecContext *cxt,
278
                  JsonPathItem *jsp, JsonbValue *jb, bool canHaveNext);
279
static JsonPathBool executeNestedBoolItem(JsonPathExecContext *cxt,
280
                      JsonPathItem *jsp, JsonbValue *jb);
281
static JsonPathExecResult executeAnyItem(JsonPathExecContext *cxt,
282
                     JsonPathItem *jsp, JsonbContainer *jbc, JsonValueList *found,
283
                     uint32 level, uint32 first, uint32 last,
284
                     bool ignoreStructuralErrors, bool unwrapNext);
285
static JsonPathBool executePredicate(JsonPathExecContext *cxt,
286
                   JsonPathItem *pred, JsonPathItem *larg, JsonPathItem *rarg,
287
                   JsonbValue *jb, bool unwrapRightArg,
288
                   JsonPathPredicateCallback exec, void *param);
289
static JsonPathExecResult executeBinaryArithmExpr(JsonPathExecContext *cxt,
290
                          JsonPathItem *jsp, JsonbValue *jb,
291
                          BinaryArithmFunc func, JsonValueList *found);
292
static JsonPathExecResult executeUnaryArithmExpr(JsonPathExecContext *cxt,
293
                         JsonPathItem *jsp, JsonbValue *jb, PGFunction func,
294
                         JsonValueList *found);
295
static JsonPathBool executeStartsWith(JsonPathItem *jsp,
296
                    JsonbValue *whole, JsonbValue *initial, void *param);
297
static JsonPathBool executeLikeRegex(JsonPathItem *jsp, JsonbValue *str,
298
                   JsonbValue *rarg, void *param);
299
static JsonPathExecResult executeNumericItemMethod(JsonPathExecContext *cxt,
300
                           JsonPathItem *jsp, JsonbValue *jb, bool unwrap, PGFunction func,
301
                           JsonValueList *found);
302
static JsonPathExecResult executeDateTimeMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
303
                        JsonbValue *jb, JsonValueList *found);
304
static JsonPathExecResult executeKeyValueMethod(JsonPathExecContext *cxt,
305
                        JsonPathItem *jsp, JsonbValue *jb, JsonValueList *found);
306
static JsonPathExecResult appendBoolResult(JsonPathExecContext *cxt,
307
                       JsonPathItem *jsp, JsonValueList *found, JsonPathBool res);
308
static void getJsonPathItem(JsonPathExecContext *cxt, JsonPathItem *item,
309
              JsonbValue *value);
310
static JsonbValue *GetJsonPathVar(void *cxt, char *varName, int varNameLen,
311
                  JsonbValue *baseObject, int *baseObjectId);
312
static int  CountJsonPathVars(void *cxt);
313
static void JsonItemFromDatum(Datum val, Oid typid, int32 typmod,
314
                JsonbValue *res);
315
static void JsonbValueInitNumericDatum(JsonbValue *jbv, Datum num);
316
static void getJsonPathVariable(JsonPathExecContext *cxt,
317
                JsonPathItem *variable, JsonbValue *value);
318
static int  countVariablesFromJsonb(void *varsJsonb);
319
static JsonbValue *getJsonPathVariableFromJsonb(void *varsJsonb, char *varName,
320
                        int varNameLength,
321
                        JsonbValue *baseObject,
322
                        int *baseObjectId);
323
static int  JsonbArraySize(JsonbValue *jb);
324
static JsonPathBool executeComparison(JsonPathItem *cmp, JsonbValue *lv,
325
                    JsonbValue *rv, void *p);
326
static JsonPathBool compareItems(int32 op, JsonbValue *jb1, JsonbValue *jb2,
327
                 bool useTz);
328
static int  compareNumeric(Numeric a, Numeric b);
329
static JsonbValue *copyJsonbValue(JsonbValue *src);
330
static JsonPathExecResult getArrayIndex(JsonPathExecContext *cxt,
331
                    JsonPathItem *jsp, JsonbValue *jb, int32 *index);
332
static JsonBaseObjectInfo setBaseObject(JsonPathExecContext *cxt,
333
                    JsonbValue *jbv, int32 id);
334
static void JsonValueListClear(JsonValueList *jvl);
335
static void JsonValueListAppend(JsonValueList *jvl, JsonbValue *jbv);
336
static int  JsonValueListLength(const JsonValueList *jvl);
337
static bool JsonValueListIsEmpty(JsonValueList *jvl);
338
static JsonbValue *JsonValueListHead(JsonValueList *jvl);
339
static List *JsonValueListGetList(JsonValueList *jvl);
340
static void JsonValueListInitIterator(const JsonValueList *jvl,
341
                    JsonValueListIterator *it);
342
static JsonbValue *JsonValueListNext(const JsonValueList *jvl,
343
                   JsonValueListIterator *it);
344
static JsonbValue *JsonbInitBinary(JsonbValue *jbv, Jsonb *jb);
345
static int  JsonbType(JsonbValue *jb);
346
static JsonbValue *getScalar(JsonbValue *scalar, enum jbvType type);
347
static JsonbValue *wrapItemsInArray(const JsonValueList *items);
348
static int  compareDatetime(Datum val1, Oid typid1, Datum val2, Oid typid2,
349
              bool useTz, bool *cast_error);
350
static void checkTimezoneIsUsedForCast(bool useTz, const char *type1,
351
                     const char *type2);
352
353
static void JsonTableInitOpaque(TableFuncScanState *state, int natts);
354
static JsonTablePlanState *JsonTableInitPlan(JsonTableExecContext *cxt,
355
                       JsonTablePlan *plan,
356
                       JsonTablePlanState *parentstate,
357
                       List *args,
358
                       MemoryContext mcxt);
359
static void JsonTableSetDocument(TableFuncScanState *state, Datum value);
360
static void JsonTableResetRowPattern(JsonTablePlanState *planstate, Datum item);
361
static bool JsonTableFetchRow(TableFuncScanState *state);
362
static Datum JsonTableGetValue(TableFuncScanState *state, int colnum,
363
                 Oid typid, int32 typmod, bool *isnull);
364
static void JsonTableDestroyOpaque(TableFuncScanState *state);
365
static bool JsonTablePlanScanNextRow(JsonTablePlanState *planstate);
366
static void JsonTableResetNestedPlan(JsonTablePlanState *planstate);
367
static bool JsonTablePlanJoinNextRow(JsonTablePlanState *planstate);
368
static bool JsonTablePlanNextRow(JsonTablePlanState *planstate);
369
370
const TableFuncRoutine JsonbTableRoutine =
371
{
372
  .InitOpaque = JsonTableInitOpaque,
373
  .SetDocument = JsonTableSetDocument,
374
  .SetNamespace = NULL,
375
  .SetRowFilter = NULL,
376
  .SetColumnFilter = NULL,
377
  .FetchRow = JsonTableFetchRow,
378
  .GetValue = JsonTableGetValue,
379
  .DestroyOpaque = JsonTableDestroyOpaque
380
};
381
382
/****************** User interface to JsonPath executor ********************/
383
384
/*
385
 * jsonb_path_exists
386
 *    Returns true if jsonpath returns at least one item for the specified
387
 *    jsonb value.  This function and jsonb_path_match() are used to
388
 *    implement @? and @@ operators, which in turn are intended to have an
389
 *    index support.  Thus, it's desirable to make it easier to achieve
390
 *    consistency between index scan results and sequential scan results.
391
 *    So, we throw as few errors as possible.  Regarding this function,
392
 *    such behavior also matches behavior of JSON_EXISTS() clause of
393
 *    SQL/JSON.  Regarding jsonb_path_match(), this function doesn't have
394
 *    an analogy in SQL/JSON, so we define its behavior on our own.
395
 */
396
static Datum
397
jsonb_path_exists_internal(FunctionCallInfo fcinfo, bool tz)
398
0
{
399
0
  Jsonb    *jb = PG_GETARG_JSONB_P(0);
400
0
  JsonPath   *jp = PG_GETARG_JSONPATH_P(1);
401
0
  JsonPathExecResult res;
402
0
  Jsonb    *vars = NULL;
403
0
  bool    silent = true;
404
405
0
  if (PG_NARGS() == 4)
406
0
  {
407
0
    vars = PG_GETARG_JSONB_P(2);
408
0
    silent = PG_GETARG_BOOL(3);
409
0
  }
410
411
0
  res = executeJsonPath(jp, vars, getJsonPathVariableFromJsonb,
412
0
              countVariablesFromJsonb,
413
0
              jb, !silent, NULL, tz);
414
415
0
  PG_FREE_IF_COPY(jb, 0);
416
0
  PG_FREE_IF_COPY(jp, 1);
417
418
0
  if (jperIsError(res))
419
0
    PG_RETURN_NULL();
420
421
0
  PG_RETURN_BOOL(res == jperOk);
422
0
}
423
424
Datum
425
jsonb_path_exists(PG_FUNCTION_ARGS)
426
0
{
427
0
  return jsonb_path_exists_internal(fcinfo, false);
428
0
}
429
430
Datum
431
jsonb_path_exists_tz(PG_FUNCTION_ARGS)
432
0
{
433
0
  return jsonb_path_exists_internal(fcinfo, true);
434
0
}
435
436
/*
437
 * jsonb_path_exists_opr
438
 *    Implementation of operator "jsonb @? jsonpath" (2-argument version of
439
 *    jsonb_path_exists()).
440
 */
441
Datum
442
jsonb_path_exists_opr(PG_FUNCTION_ARGS)
443
0
{
444
  /* just call the other one -- it can handle both cases */
445
0
  return jsonb_path_exists_internal(fcinfo, false);
446
0
}
447
448
/*
449
 * jsonb_path_match
450
 *    Returns jsonpath predicate result item for the specified jsonb value.
451
 *    See jsonb_path_exists() comment for details regarding error handling.
452
 */
453
static Datum
454
jsonb_path_match_internal(FunctionCallInfo fcinfo, bool tz)
455
0
{
456
0
  Jsonb    *jb = PG_GETARG_JSONB_P(0);
457
0
  JsonPath   *jp = PG_GETARG_JSONPATH_P(1);
458
0
  JsonValueList found = {0};
459
0
  Jsonb    *vars = NULL;
460
0
  bool    silent = true;
461
462
0
  if (PG_NARGS() == 4)
463
0
  {
464
0
    vars = PG_GETARG_JSONB_P(2);
465
0
    silent = PG_GETARG_BOOL(3);
466
0
  }
467
468
0
  (void) executeJsonPath(jp, vars, getJsonPathVariableFromJsonb,
469
0
               countVariablesFromJsonb,
470
0
               jb, !silent, &found, tz);
471
472
0
  PG_FREE_IF_COPY(jb, 0);
473
0
  PG_FREE_IF_COPY(jp, 1);
474
475
0
  if (JsonValueListLength(&found) == 1)
476
0
  {
477
0
    JsonbValue *jbv = JsonValueListHead(&found);
478
479
0
    if (jbv->type == jbvBool)
480
0
      PG_RETURN_BOOL(jbv->val.boolean);
481
482
0
    if (jbv->type == jbvNull)
483
0
      PG_RETURN_NULL();
484
0
  }
485
486
0
  if (!silent)
487
0
    ereport(ERROR,
488
0
        (errcode(ERRCODE_SINGLETON_SQL_JSON_ITEM_REQUIRED),
489
0
         errmsg("single boolean result is expected")));
490
491
0
  PG_RETURN_NULL();
492
0
}
493
494
Datum
495
jsonb_path_match(PG_FUNCTION_ARGS)
496
0
{
497
0
  return jsonb_path_match_internal(fcinfo, false);
498
0
}
499
500
Datum
501
jsonb_path_match_tz(PG_FUNCTION_ARGS)
502
0
{
503
0
  return jsonb_path_match_internal(fcinfo, true);
504
0
}
505
506
/*
507
 * jsonb_path_match_opr
508
 *    Implementation of operator "jsonb @@ jsonpath" (2-argument version of
509
 *    jsonb_path_match()).
510
 */
511
Datum
512
jsonb_path_match_opr(PG_FUNCTION_ARGS)
513
0
{
514
  /* just call the other one -- it can handle both cases */
515
0
  return jsonb_path_match_internal(fcinfo, false);
516
0
}
517
518
/*
519
 * jsonb_path_query
520
 *    Executes jsonpath for given jsonb document and returns result as
521
 *    rowset.
522
 */
523
static Datum
524
jsonb_path_query_internal(FunctionCallInfo fcinfo, bool tz)
525
0
{
526
0
  FuncCallContext *funcctx;
527
0
  List     *found;
528
0
  JsonbValue *v;
529
0
  ListCell   *c;
530
531
0
  if (SRF_IS_FIRSTCALL())
532
0
  {
533
0
    JsonPath   *jp;
534
0
    Jsonb    *jb;
535
0
    MemoryContext oldcontext;
536
0
    Jsonb    *vars;
537
0
    bool    silent;
538
0
    JsonValueList found = {0};
539
540
0
    funcctx = SRF_FIRSTCALL_INIT();
541
0
    oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
542
543
0
    jb = PG_GETARG_JSONB_P_COPY(0);
544
0
    jp = PG_GETARG_JSONPATH_P_COPY(1);
545
0
    vars = PG_GETARG_JSONB_P_COPY(2);
546
0
    silent = PG_GETARG_BOOL(3);
547
548
0
    (void) executeJsonPath(jp, vars, getJsonPathVariableFromJsonb,
549
0
                 countVariablesFromJsonb,
550
0
                 jb, !silent, &found, tz);
551
552
0
    funcctx->user_fctx = JsonValueListGetList(&found);
553
554
0
    MemoryContextSwitchTo(oldcontext);
555
0
  }
556
557
0
  funcctx = SRF_PERCALL_SETUP();
558
0
  found = funcctx->user_fctx;
559
560
0
  c = list_head(found);
561
562
0
  if (c == NULL)
563
0
    SRF_RETURN_DONE(funcctx);
564
565
0
  v = lfirst(c);
566
0
  funcctx->user_fctx = list_delete_first(found);
567
568
0
  SRF_RETURN_NEXT(funcctx, JsonbPGetDatum(JsonbValueToJsonb(v)));
569
0
}
570
571
Datum
572
jsonb_path_query(PG_FUNCTION_ARGS)
573
0
{
574
0
  return jsonb_path_query_internal(fcinfo, false);
575
0
}
576
577
Datum
578
jsonb_path_query_tz(PG_FUNCTION_ARGS)
579
0
{
580
0
  return jsonb_path_query_internal(fcinfo, true);
581
0
}
582
583
/*
584
 * jsonb_path_query_array
585
 *    Executes jsonpath for given jsonb document and returns result as
586
 *    jsonb array.
587
 */
588
static Datum
589
jsonb_path_query_array_internal(FunctionCallInfo fcinfo, bool tz)
590
0
{
591
0
  Jsonb    *jb = PG_GETARG_JSONB_P(0);
592
0
  JsonPath   *jp = PG_GETARG_JSONPATH_P(1);
593
0
  JsonValueList found = {0};
594
0
  Jsonb    *vars = PG_GETARG_JSONB_P(2);
595
0
  bool    silent = PG_GETARG_BOOL(3);
596
597
0
  (void) executeJsonPath(jp, vars, getJsonPathVariableFromJsonb,
598
0
               countVariablesFromJsonb,
599
0
               jb, !silent, &found, tz);
600
601
0
  PG_RETURN_JSONB_P(JsonbValueToJsonb(wrapItemsInArray(&found)));
602
0
}
603
604
Datum
605
jsonb_path_query_array(PG_FUNCTION_ARGS)
606
0
{
607
0
  return jsonb_path_query_array_internal(fcinfo, false);
608
0
}
609
610
Datum
611
jsonb_path_query_array_tz(PG_FUNCTION_ARGS)
612
0
{
613
0
  return jsonb_path_query_array_internal(fcinfo, true);
614
0
}
615
616
/*
617
 * jsonb_path_query_first
618
 *    Executes jsonpath for given jsonb document and returns first result
619
 *    item.  If there are no items, NULL returned.
620
 */
621
static Datum
622
jsonb_path_query_first_internal(FunctionCallInfo fcinfo, bool tz)
623
0
{
624
0
  Jsonb    *jb = PG_GETARG_JSONB_P(0);
625
0
  JsonPath   *jp = PG_GETARG_JSONPATH_P(1);
626
0
  JsonValueList found = {0};
627
0
  Jsonb    *vars = PG_GETARG_JSONB_P(2);
628
0
  bool    silent = PG_GETARG_BOOL(3);
629
630
0
  (void) executeJsonPath(jp, vars, getJsonPathVariableFromJsonb,
631
0
               countVariablesFromJsonb,
632
0
               jb, !silent, &found, tz);
633
634
0
  if (JsonValueListLength(&found) >= 1)
635
0
    PG_RETURN_JSONB_P(JsonbValueToJsonb(JsonValueListHead(&found)));
636
0
  else
637
0
    PG_RETURN_NULL();
638
0
}
639
640
Datum
641
jsonb_path_query_first(PG_FUNCTION_ARGS)
642
0
{
643
0
  return jsonb_path_query_first_internal(fcinfo, false);
644
0
}
645
646
Datum
647
jsonb_path_query_first_tz(PG_FUNCTION_ARGS)
648
0
{
649
0
  return jsonb_path_query_first_internal(fcinfo, true);
650
0
}
651
652
/********************Execute functions for JsonPath**************************/
653
654
/*
655
 * Interface to jsonpath executor
656
 *
657
 * 'path' - jsonpath to be executed
658
 * 'vars' - variables to be substituted to jsonpath
659
 * 'getVar' - callback used by getJsonPathVariable() to extract variables from
660
 *    'vars'
661
 * 'countVars' - callback to count the number of jsonpath variables in 'vars'
662
 * 'json' - target document for jsonpath evaluation
663
 * 'throwErrors' - whether we should throw suppressible errors
664
 * 'result' - list to store result items into
665
 *
666
 * Returns an error if a recoverable error happens during processing, or NULL
667
 * on no error.
668
 *
669
 * Note, jsonb and jsonpath values should be available and untoasted during
670
 * work because JsonPathItem, JsonbValue and result item could have pointers
671
 * into input values.  If caller needs to just check if document matches
672
 * jsonpath, then it doesn't provide a result arg.  In this case executor
673
 * works till first positive result and does not check the rest if possible.
674
 * In other case it tries to find all the satisfied result items.
675
 */
676
static JsonPathExecResult
677
executeJsonPath(JsonPath *path, void *vars, JsonPathGetVarCallback getVar,
678
        JsonPathCountVarsCallback countVars,
679
        Jsonb *json, bool throwErrors, JsonValueList *result,
680
        bool useTz)
681
0
{
682
0
  JsonPathExecContext cxt;
683
0
  JsonPathExecResult res;
684
0
  JsonPathItem jsp;
685
0
  JsonbValue  jbv;
686
687
0
  jspInit(&jsp, path);
688
689
0
  if (!JsonbExtractScalar(&json->root, &jbv))
690
0
    JsonbInitBinary(&jbv, json);
691
692
0
  cxt.vars = vars;
693
0
  cxt.getVar = getVar;
694
0
  cxt.laxMode = (path->header & JSONPATH_LAX) != 0;
695
0
  cxt.ignoreStructuralErrors = cxt.laxMode;
696
0
  cxt.root = &jbv;
697
0
  cxt.current = &jbv;
698
0
  cxt.baseObject.jbc = NULL;
699
0
  cxt.baseObject.id = 0;
700
  /* 1 + number of base objects in vars */
701
0
  cxt.lastGeneratedObjectId = 1 + countVars(vars);
702
0
  cxt.innermostArraySize = -1;
703
0
  cxt.throwErrors = throwErrors;
704
0
  cxt.useTz = useTz;
705
706
0
  if (jspStrictAbsenceOfErrors(&cxt) && !result)
707
0
  {
708
    /*
709
     * In strict mode we must get a complete list of values to check that
710
     * there are no errors at all.
711
     */
712
0
    JsonValueList vals = {0};
713
714
0
    res = executeItem(&cxt, &jsp, &jbv, &vals);
715
716
0
    if (jperIsError(res))
717
0
      return res;
718
719
0
    return JsonValueListIsEmpty(&vals) ? jperNotFound : jperOk;
720
0
  }
721
722
0
  res = executeItem(&cxt, &jsp, &jbv, result);
723
724
0
  Assert(!throwErrors || !jperIsError(res));
725
726
0
  return res;
727
0
}
728
729
/*
730
 * Execute jsonpath with automatic unwrapping of current item in lax mode.
731
 */
732
static JsonPathExecResult
733
executeItem(JsonPathExecContext *cxt, JsonPathItem *jsp,
734
      JsonbValue *jb, JsonValueList *found)
735
0
{
736
0
  return executeItemOptUnwrapTarget(cxt, jsp, jb, found, jspAutoUnwrap(cxt));
737
0
}
738
739
/*
740
 * Main jsonpath executor function: walks on jsonpath structure, finds
741
 * relevant parts of jsonb and evaluates expressions over them.
742
 * When 'unwrap' is true current SQL/JSON item is unwrapped if it is an array.
743
 */
744
static JsonPathExecResult
745
executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
746
               JsonbValue *jb, JsonValueList *found, bool unwrap)
747
0
{
748
0
  JsonPathItem elem;
749
0
  JsonPathExecResult res = jperNotFound;
750
0
  JsonBaseObjectInfo baseObject;
751
752
0
  check_stack_depth();
753
0
  CHECK_FOR_INTERRUPTS();
754
755
0
  switch (jsp->type)
756
0
  {
757
0
    case jpiNull:
758
0
    case jpiBool:
759
0
    case jpiNumeric:
760
0
    case jpiString:
761
0
    case jpiVariable:
762
0
      {
763
0
        JsonbValue  vbuf;
764
0
        JsonbValue *v;
765
0
        bool    hasNext = jspGetNext(jsp, &elem);
766
767
0
        if (!hasNext && !found && jsp->type != jpiVariable)
768
0
        {
769
          /*
770
           * Skip evaluation, but not for variables.  We must
771
           * trigger an error for the missing variable.
772
           */
773
0
          res = jperOk;
774
0
          break;
775
0
        }
776
777
0
        v = hasNext ? &vbuf : palloc(sizeof(*v));
778
779
0
        baseObject = cxt->baseObject;
780
0
        getJsonPathItem(cxt, jsp, v);
781
782
0
        res = executeNextItem(cxt, jsp, &elem,
783
0
                    v, found, hasNext);
784
0
        cxt->baseObject = baseObject;
785
0
      }
786
0
      break;
787
788
      /* all boolean item types: */
789
0
    case jpiAnd:
790
0
    case jpiOr:
791
0
    case jpiNot:
792
0
    case jpiIsUnknown:
793
0
    case jpiEqual:
794
0
    case jpiNotEqual:
795
0
    case jpiLess:
796
0
    case jpiGreater:
797
0
    case jpiLessOrEqual:
798
0
    case jpiGreaterOrEqual:
799
0
    case jpiExists:
800
0
    case jpiStartsWith:
801
0
    case jpiLikeRegex:
802
0
      {
803
0
        JsonPathBool st = executeBoolItem(cxt, jsp, jb, true);
804
805
0
        res = appendBoolResult(cxt, jsp, found, st);
806
0
        break;
807
0
      }
808
809
0
    case jpiAdd:
810
0
      return executeBinaryArithmExpr(cxt, jsp, jb,
811
0
                       numeric_add_opt_error, found);
812
813
0
    case jpiSub:
814
0
      return executeBinaryArithmExpr(cxt, jsp, jb,
815
0
                       numeric_sub_opt_error, found);
816
817
0
    case jpiMul:
818
0
      return executeBinaryArithmExpr(cxt, jsp, jb,
819
0
                       numeric_mul_opt_error, found);
820
821
0
    case jpiDiv:
822
0
      return executeBinaryArithmExpr(cxt, jsp, jb,
823
0
                       numeric_div_opt_error, found);
824
825
0
    case jpiMod:
826
0
      return executeBinaryArithmExpr(cxt, jsp, jb,
827
0
                       numeric_mod_opt_error, found);
828
829
0
    case jpiPlus:
830
0
      return executeUnaryArithmExpr(cxt, jsp, jb, NULL, found);
831
832
0
    case jpiMinus:
833
0
      return executeUnaryArithmExpr(cxt, jsp, jb, numeric_uminus,
834
0
                      found);
835
836
0
    case jpiAnyArray:
837
0
      if (JsonbType(jb) == jbvArray)
838
0
      {
839
0
        bool    hasNext = jspGetNext(jsp, &elem);
840
841
0
        res = executeItemUnwrapTargetArray(cxt, hasNext ? &elem : NULL,
842
0
                           jb, found, jspAutoUnwrap(cxt));
843
0
      }
844
0
      else if (jspAutoWrap(cxt))
845
0
        res = executeNextItem(cxt, jsp, NULL, jb, found, true);
846
0
      else if (!jspIgnoreStructuralErrors(cxt))
847
0
        RETURN_ERROR(ereport(ERROR,
848
0
                   (errcode(ERRCODE_SQL_JSON_ARRAY_NOT_FOUND),
849
0
                    errmsg("jsonpath wildcard array accessor can only be applied to an array"))));
850
0
      break;
851
852
0
    case jpiAnyKey:
853
0
      if (JsonbType(jb) == jbvObject)
854
0
      {
855
0
        bool    hasNext = jspGetNext(jsp, &elem);
856
857
0
        if (jb->type != jbvBinary)
858
0
          elog(ERROR, "invalid jsonb object type: %d", jb->type);
859
860
0
        return executeAnyItem
861
0
          (cxt, hasNext ? &elem : NULL,
862
0
           jb->val.binary.data, found, 1, 1, 1,
863
0
           false, jspAutoUnwrap(cxt));
864
0
      }
865
0
      else if (unwrap && JsonbType(jb) == jbvArray)
866
0
        return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
867
0
      else if (!jspIgnoreStructuralErrors(cxt))
868
0
      {
869
0
        Assert(found);
870
0
        RETURN_ERROR(ereport(ERROR,
871
0
                   (errcode(ERRCODE_SQL_JSON_OBJECT_NOT_FOUND),
872
0
                    errmsg("jsonpath wildcard member accessor can only be applied to an object"))));
873
0
      }
874
0
      break;
875
876
0
    case jpiIndexArray:
877
0
      if (JsonbType(jb) == jbvArray || jspAutoWrap(cxt))
878
0
      {
879
0
        int     innermostArraySize = cxt->innermostArraySize;
880
0
        int     i;
881
0
        int     size = JsonbArraySize(jb);
882
0
        bool    singleton = size < 0;
883
0
        bool    hasNext = jspGetNext(jsp, &elem);
884
885
0
        if (singleton)
886
0
          size = 1;
887
888
0
        cxt->innermostArraySize = size; /* for LAST evaluation */
889
890
0
        for (i = 0; i < jsp->content.array.nelems; i++)
891
0
        {
892
0
          JsonPathItem from;
893
0
          JsonPathItem to;
894
0
          int32   index;
895
0
          int32   index_from;
896
0
          int32   index_to;
897
0
          bool    range = jspGetArraySubscript(jsp, &from,
898
0
                               &to, i);
899
900
0
          res = getArrayIndex(cxt, &from, jb, &index_from);
901
902
0
          if (jperIsError(res))
903
0
            break;
904
905
0
          if (range)
906
0
          {
907
0
            res = getArrayIndex(cxt, &to, jb, &index_to);
908
909
0
            if (jperIsError(res))
910
0
              break;
911
0
          }
912
0
          else
913
0
            index_to = index_from;
914
915
0
          if (!jspIgnoreStructuralErrors(cxt) &&
916
0
            (index_from < 0 ||
917
0
             index_from > index_to ||
918
0
             index_to >= size))
919
0
            RETURN_ERROR(ereport(ERROR,
920
0
                       (errcode(ERRCODE_INVALID_SQL_JSON_SUBSCRIPT),
921
0
                        errmsg("jsonpath array subscript is out of bounds"))));
922
923
0
          if (index_from < 0)
924
0
            index_from = 0;
925
926
0
          if (index_to >= size)
927
0
            index_to = size - 1;
928
929
0
          res = jperNotFound;
930
931
0
          for (index = index_from; index <= index_to; index++)
932
0
          {
933
0
            JsonbValue *v;
934
0
            bool    copy;
935
936
0
            if (singleton)
937
0
            {
938
0
              v = jb;
939
0
              copy = true;
940
0
            }
941
0
            else
942
0
            {
943
0
              v = getIthJsonbValueFromContainer(jb->val.binary.data,
944
0
                                (uint32) index);
945
946
0
              if (v == NULL)
947
0
                continue;
948
949
0
              copy = false;
950
0
            }
951
952
0
            if (!hasNext && !found)
953
0
              return jperOk;
954
955
0
            res = executeNextItem(cxt, jsp, &elem, v, found,
956
0
                        copy);
957
958
0
            if (jperIsError(res))
959
0
              break;
960
961
0
            if (res == jperOk && !found)
962
0
              break;
963
0
          }
964
965
0
          if (jperIsError(res))
966
0
            break;
967
968
0
          if (res == jperOk && !found)
969
0
            break;
970
0
        }
971
972
0
        cxt->innermostArraySize = innermostArraySize;
973
0
      }
974
0
      else if (!jspIgnoreStructuralErrors(cxt))
975
0
      {
976
0
        RETURN_ERROR(ereport(ERROR,
977
0
                   (errcode(ERRCODE_SQL_JSON_ARRAY_NOT_FOUND),
978
0
                    errmsg("jsonpath array accessor can only be applied to an array"))));
979
0
      }
980
0
      break;
981
982
0
    case jpiAny:
983
0
      {
984
0
        bool    hasNext = jspGetNext(jsp, &elem);
985
986
        /* first try without any intermediate steps */
987
0
        if (jsp->content.anybounds.first == 0)
988
0
        {
989
0
          bool    savedIgnoreStructuralErrors;
990
991
0
          savedIgnoreStructuralErrors = cxt->ignoreStructuralErrors;
992
0
          cxt->ignoreStructuralErrors = true;
993
0
          res = executeNextItem(cxt, jsp, &elem,
994
0
                      jb, found, true);
995
0
          cxt->ignoreStructuralErrors = savedIgnoreStructuralErrors;
996
997
0
          if (res == jperOk && !found)
998
0
            break;
999
0
        }
1000
1001
0
        if (jb->type == jbvBinary)
1002
0
          res = executeAnyItem
1003
0
            (cxt, hasNext ? &elem : NULL,
1004
0
             jb->val.binary.data, found,
1005
0
             1,
1006
0
             jsp->content.anybounds.first,
1007
0
             jsp->content.anybounds.last,
1008
0
             true, jspAutoUnwrap(cxt));
1009
0
        break;
1010
0
      }
1011
1012
0
    case jpiKey:
1013
0
      if (JsonbType(jb) == jbvObject)
1014
0
      {
1015
0
        JsonbValue *v;
1016
0
        JsonbValue  key;
1017
1018
0
        key.type = jbvString;
1019
0
        key.val.string.val = jspGetString(jsp, &key.val.string.len);
1020
1021
0
        v = findJsonbValueFromContainer(jb->val.binary.data,
1022
0
                        JB_FOBJECT, &key);
1023
1024
0
        if (v != NULL)
1025
0
        {
1026
0
          res = executeNextItem(cxt, jsp, NULL,
1027
0
                      v, found, false);
1028
1029
          /* free value if it was not added to found list */
1030
0
          if (jspHasNext(jsp) || !found)
1031
0
            pfree(v);
1032
0
        }
1033
0
        else if (!jspIgnoreStructuralErrors(cxt))
1034
0
        {
1035
0
          Assert(found);
1036
1037
0
          if (!jspThrowErrors(cxt))
1038
0
            return jperError;
1039
1040
0
          ereport(ERROR,
1041
0
              (errcode(ERRCODE_SQL_JSON_MEMBER_NOT_FOUND), \
1042
0
               errmsg("JSON object does not contain key \"%s\"",
1043
0
                  pnstrdup(key.val.string.val,
1044
0
                       key.val.string.len))));
1045
0
        }
1046
0
      }
1047
0
      else if (unwrap && JsonbType(jb) == jbvArray)
1048
0
        return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
1049
0
      else if (!jspIgnoreStructuralErrors(cxt))
1050
0
      {
1051
0
        Assert(found);
1052
0
        RETURN_ERROR(ereport(ERROR,
1053
0
                   (errcode(ERRCODE_SQL_JSON_MEMBER_NOT_FOUND),
1054
0
                    errmsg("jsonpath member accessor can only be applied to an object"))));
1055
0
      }
1056
0
      break;
1057
1058
0
    case jpiCurrent:
1059
0
      res = executeNextItem(cxt, jsp, NULL, cxt->current,
1060
0
                  found, true);
1061
0
      break;
1062
1063
0
    case jpiRoot:
1064
0
      jb = cxt->root;
1065
0
      baseObject = setBaseObject(cxt, jb, 0);
1066
0
      res = executeNextItem(cxt, jsp, NULL, jb, found, true);
1067
0
      cxt->baseObject = baseObject;
1068
0
      break;
1069
1070
0
    case jpiFilter:
1071
0
      {
1072
0
        JsonPathBool st;
1073
1074
0
        if (unwrap && JsonbType(jb) == jbvArray)
1075
0
          return executeItemUnwrapTargetArray(cxt, jsp, jb, found,
1076
0
                            false);
1077
1078
0
        jspGetArg(jsp, &elem);
1079
0
        st = executeNestedBoolItem(cxt, &elem, jb);
1080
0
        if (st != jpbTrue)
1081
0
          res = jperNotFound;
1082
0
        else
1083
0
          res = executeNextItem(cxt, jsp, NULL,
1084
0
                      jb, found, true);
1085
0
        break;
1086
0
      }
1087
1088
0
    case jpiType:
1089
0
      {
1090
0
        JsonbValue *jbv = palloc(sizeof(*jbv));
1091
1092
0
        jbv->type = jbvString;
1093
0
        jbv->val.string.val = pstrdup(JsonbTypeName(jb));
1094
0
        jbv->val.string.len = strlen(jbv->val.string.val);
1095
1096
0
        res = executeNextItem(cxt, jsp, NULL, jbv,
1097
0
                    found, false);
1098
0
      }
1099
0
      break;
1100
1101
0
    case jpiSize:
1102
0
      {
1103
0
        int     size = JsonbArraySize(jb);
1104
1105
0
        if (size < 0)
1106
0
        {
1107
0
          if (!jspAutoWrap(cxt))
1108
0
          {
1109
0
            if (!jspIgnoreStructuralErrors(cxt))
1110
0
              RETURN_ERROR(ereport(ERROR,
1111
0
                         (errcode(ERRCODE_SQL_JSON_ARRAY_NOT_FOUND),
1112
0
                          errmsg("jsonpath item method .%s() can only be applied to an array",
1113
0
                             jspOperationName(jsp->type)))));
1114
0
            break;
1115
0
          }
1116
1117
0
          size = 1;
1118
0
        }
1119
1120
0
        jb = palloc(sizeof(*jb));
1121
1122
0
        jb->type = jbvNumeric;
1123
0
        jb->val.numeric = int64_to_numeric(size);
1124
1125
0
        res = executeNextItem(cxt, jsp, NULL, jb, found, false);
1126
0
      }
1127
0
      break;
1128
1129
0
    case jpiAbs:
1130
0
      return executeNumericItemMethod(cxt, jsp, jb, unwrap, numeric_abs,
1131
0
                      found);
1132
1133
0
    case jpiFloor:
1134
0
      return executeNumericItemMethod(cxt, jsp, jb, unwrap, numeric_floor,
1135
0
                      found);
1136
1137
0
    case jpiCeiling:
1138
0
      return executeNumericItemMethod(cxt, jsp, jb, unwrap, numeric_ceil,
1139
0
                      found);
1140
1141
0
    case jpiDouble:
1142
0
      {
1143
0
        JsonbValue  jbv;
1144
1145
0
        if (unwrap && JsonbType(jb) == jbvArray)
1146
0
          return executeItemUnwrapTargetArray(cxt, jsp, jb, found,
1147
0
                            false);
1148
1149
0
        if (jb->type == jbvNumeric)
1150
0
        {
1151
0
          char     *tmp = DatumGetCString(DirectFunctionCall1(numeric_out,
1152
0
                                      NumericGetDatum(jb->val.numeric)));
1153
0
          double    val;
1154
0
          ErrorSaveContext escontext = {T_ErrorSaveContext};
1155
1156
0
          val = float8in_internal(tmp,
1157
0
                      NULL,
1158
0
                      "double precision",
1159
0
                      tmp,
1160
0
                      (Node *) &escontext);
1161
1162
0
          if (escontext.error_occurred)
1163
0
            RETURN_ERROR(ereport(ERROR,
1164
0
                       (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1165
0
                        errmsg("argument \"%s\" of jsonpath item method .%s() is invalid for type %s",
1166
0
                           tmp, jspOperationName(jsp->type), "double precision"))));
1167
0
          if (isinf(val) || isnan(val))
1168
0
            RETURN_ERROR(ereport(ERROR,
1169
0
                       (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1170
0
                        errmsg("NaN or Infinity is not allowed for jsonpath item method .%s()",
1171
0
                           jspOperationName(jsp->type)))));
1172
0
          res = jperOk;
1173
0
        }
1174
0
        else if (jb->type == jbvString)
1175
0
        {
1176
          /* cast string as double */
1177
0
          double    val;
1178
0
          char     *tmp = pnstrdup(jb->val.string.val,
1179
0
                         jb->val.string.len);
1180
0
          ErrorSaveContext escontext = {T_ErrorSaveContext};
1181
1182
0
          val = float8in_internal(tmp,
1183
0
                      NULL,
1184
0
                      "double precision",
1185
0
                      tmp,
1186
0
                      (Node *) &escontext);
1187
1188
0
          if (escontext.error_occurred)
1189
0
            RETURN_ERROR(ereport(ERROR,
1190
0
                       (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1191
0
                        errmsg("argument \"%s\" of jsonpath item method .%s() is invalid for type %s",
1192
0
                           tmp, jspOperationName(jsp->type), "double precision"))));
1193
0
          if (isinf(val) || isnan(val))
1194
0
            RETURN_ERROR(ereport(ERROR,
1195
0
                       (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1196
0
                        errmsg("NaN or Infinity is not allowed for jsonpath item method .%s()",
1197
0
                           jspOperationName(jsp->type)))));
1198
1199
0
          jb = &jbv;
1200
0
          jb->type = jbvNumeric;
1201
0
          jb->val.numeric = DatumGetNumeric(DirectFunctionCall1(float8_numeric,
1202
0
                                      Float8GetDatum(val)));
1203
0
          res = jperOk;
1204
0
        }
1205
1206
0
        if (res == jperNotFound)
1207
0
          RETURN_ERROR(ereport(ERROR,
1208
0
                     (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1209
0
                      errmsg("jsonpath item method .%s() can only be applied to a string or numeric value",
1210
0
                         jspOperationName(jsp->type)))));
1211
1212
0
        res = executeNextItem(cxt, jsp, NULL, jb, found, true);
1213
0
      }
1214
0
      break;
1215
1216
0
    case jpiDatetime:
1217
0
    case jpiDate:
1218
0
    case jpiTime:
1219
0
    case jpiTimeTz:
1220
0
    case jpiTimestamp:
1221
0
    case jpiTimestampTz:
1222
0
      if (unwrap && JsonbType(jb) == jbvArray)
1223
0
        return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
1224
1225
0
      return executeDateTimeMethod(cxt, jsp, jb, found);
1226
1227
0
    case jpiKeyValue:
1228
0
      if (unwrap && JsonbType(jb) == jbvArray)
1229
0
        return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
1230
1231
0
      return executeKeyValueMethod(cxt, jsp, jb, found);
1232
1233
0
    case jpiLast:
1234
0
      {
1235
0
        JsonbValue  tmpjbv;
1236
0
        JsonbValue *lastjbv;
1237
0
        int     last;
1238
0
        bool    hasNext = jspGetNext(jsp, &elem);
1239
1240
0
        if (cxt->innermostArraySize < 0)
1241
0
          elog(ERROR, "evaluating jsonpath LAST outside of array subscript");
1242
1243
0
        if (!hasNext && !found)
1244
0
        {
1245
0
          res = jperOk;
1246
0
          break;
1247
0
        }
1248
1249
0
        last = cxt->innermostArraySize - 1;
1250
1251
0
        lastjbv = hasNext ? &tmpjbv : palloc(sizeof(*lastjbv));
1252
1253
0
        lastjbv->type = jbvNumeric;
1254
0
        lastjbv->val.numeric = int64_to_numeric(last);
1255
1256
0
        res = executeNextItem(cxt, jsp, &elem,
1257
0
                    lastjbv, found, hasNext);
1258
0
      }
1259
0
      break;
1260
1261
0
    case jpiBigint:
1262
0
      {
1263
0
        JsonbValue  jbv;
1264
0
        Datum   datum;
1265
1266
0
        if (unwrap && JsonbType(jb) == jbvArray)
1267
0
          return executeItemUnwrapTargetArray(cxt, jsp, jb, found,
1268
0
                            false);
1269
1270
0
        if (jb->type == jbvNumeric)
1271
0
        {
1272
0
          bool    have_error;
1273
0
          int64   val;
1274
1275
0
          val = numeric_int8_opt_error(jb->val.numeric, &have_error);
1276
0
          if (have_error)
1277
0
            RETURN_ERROR(ereport(ERROR,
1278
0
                       (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1279
0
                        errmsg("argument \"%s\" of jsonpath item method .%s() is invalid for type %s",
1280
0
                           DatumGetCString(DirectFunctionCall1(numeric_out,
1281
0
                                             NumericGetDatum(jb->val.numeric))),
1282
0
                           jspOperationName(jsp->type),
1283
0
                           "bigint"))));
1284
1285
0
          datum = Int64GetDatum(val);
1286
0
          res = jperOk;
1287
0
        }
1288
0
        else if (jb->type == jbvString)
1289
0
        {
1290
          /* cast string as bigint */
1291
0
          char     *tmp = pnstrdup(jb->val.string.val,
1292
0
                         jb->val.string.len);
1293
0
          ErrorSaveContext escontext = {T_ErrorSaveContext};
1294
0
          bool    noerr;
1295
1296
0
          noerr = DirectInputFunctionCallSafe(int8in, tmp,
1297
0
                            InvalidOid, -1,
1298
0
                            (Node *) &escontext,
1299
0
                            &datum);
1300
1301
0
          if (!noerr || escontext.error_occurred)
1302
0
            RETURN_ERROR(ereport(ERROR,
1303
0
                       (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1304
0
                        errmsg("argument \"%s\" of jsonpath item method .%s() is invalid for type %s",
1305
0
                           tmp, jspOperationName(jsp->type), "bigint"))));
1306
0
          res = jperOk;
1307
0
        }
1308
1309
0
        if (res == jperNotFound)
1310
0
          RETURN_ERROR(ereport(ERROR,
1311
0
                     (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1312
0
                      errmsg("jsonpath item method .%s() can only be applied to a string or numeric value",
1313
0
                         jspOperationName(jsp->type)))));
1314
1315
0
        jb = &jbv;
1316
0
        jb->type = jbvNumeric;
1317
0
        jb->val.numeric = DatumGetNumeric(DirectFunctionCall1(int8_numeric,
1318
0
                                    datum));
1319
1320
0
        res = executeNextItem(cxt, jsp, NULL, jb, found, true);
1321
0
      }
1322
0
      break;
1323
1324
0
    case jpiBoolean:
1325
0
      {
1326
0
        JsonbValue  jbv;
1327
0
        bool    bval;
1328
1329
0
        if (unwrap && JsonbType(jb) == jbvArray)
1330
0
          return executeItemUnwrapTargetArray(cxt, jsp, jb, found,
1331
0
                            false);
1332
1333
0
        if (jb->type == jbvBool)
1334
0
        {
1335
0
          bval = jb->val.boolean;
1336
1337
0
          res = jperOk;
1338
0
        }
1339
0
        else if (jb->type == jbvNumeric)
1340
0
        {
1341
0
          int     ival;
1342
0
          Datum   datum;
1343
0
          bool    noerr;
1344
0
          char     *tmp = DatumGetCString(DirectFunctionCall1(numeric_out,
1345
0
                                      NumericGetDatum(jb->val.numeric)));
1346
0
          ErrorSaveContext escontext = {T_ErrorSaveContext};
1347
1348
0
          noerr = DirectInputFunctionCallSafe(int4in, tmp,
1349
0
                            InvalidOid, -1,
1350
0
                            (Node *) &escontext,
1351
0
                            &datum);
1352
1353
0
          if (!noerr || escontext.error_occurred)
1354
0
            RETURN_ERROR(ereport(ERROR,
1355
0
                       (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1356
0
                        errmsg("argument \"%s\" of jsonpath item method .%s() is invalid for type %s",
1357
0
                           tmp, jspOperationName(jsp->type), "boolean"))));
1358
1359
0
          ival = DatumGetInt32(datum);
1360
0
          if (ival == 0)
1361
0
            bval = false;
1362
0
          else
1363
0
            bval = true;
1364
1365
0
          res = jperOk;
1366
0
        }
1367
0
        else if (jb->type == jbvString)
1368
0
        {
1369
          /* cast string as boolean */
1370
0
          char     *tmp = pnstrdup(jb->val.string.val,
1371
0
                         jb->val.string.len);
1372
1373
0
          if (!parse_bool(tmp, &bval))
1374
0
            RETURN_ERROR(ereport(ERROR,
1375
0
                       (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1376
0
                        errmsg("argument \"%s\" of jsonpath item method .%s() is invalid for type %s",
1377
0
                           tmp, jspOperationName(jsp->type), "boolean"))));
1378
1379
0
          res = jperOk;
1380
0
        }
1381
1382
0
        if (res == jperNotFound)
1383
0
          RETURN_ERROR(ereport(ERROR,
1384
0
                     (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1385
0
                      errmsg("jsonpath item method .%s() can only be applied to a boolean, string, or numeric value",
1386
0
                         jspOperationName(jsp->type)))));
1387
1388
0
        jb = &jbv;
1389
0
        jb->type = jbvBool;
1390
0
        jb->val.boolean = bval;
1391
1392
0
        res = executeNextItem(cxt, jsp, NULL, jb, found, true);
1393
0
      }
1394
0
      break;
1395
1396
0
    case jpiDecimal:
1397
0
    case jpiNumber:
1398
0
      {
1399
0
        JsonbValue  jbv;
1400
0
        Numeric   num;
1401
0
        char     *numstr = NULL;
1402
1403
0
        if (unwrap && JsonbType(jb) == jbvArray)
1404
0
          return executeItemUnwrapTargetArray(cxt, jsp, jb, found,
1405
0
                            false);
1406
1407
0
        if (jb->type == jbvNumeric)
1408
0
        {
1409
0
          num = jb->val.numeric;
1410
0
          if (numeric_is_nan(num) || numeric_is_inf(num))
1411
0
            RETURN_ERROR(ereport(ERROR,
1412
0
                       (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1413
0
                        errmsg("NaN or Infinity is not allowed for jsonpath item method .%s()",
1414
0
                           jspOperationName(jsp->type)))));
1415
1416
0
          if (jsp->type == jpiDecimal)
1417
0
            numstr = DatumGetCString(DirectFunctionCall1(numeric_out,
1418
0
                                   NumericGetDatum(num)));
1419
0
          res = jperOk;
1420
0
        }
1421
0
        else if (jb->type == jbvString)
1422
0
        {
1423
          /* cast string as number */
1424
0
          Datum   datum;
1425
0
          bool    noerr;
1426
0
          ErrorSaveContext escontext = {T_ErrorSaveContext};
1427
1428
0
          numstr = pnstrdup(jb->val.string.val, jb->val.string.len);
1429
1430
0
          noerr = DirectInputFunctionCallSafe(numeric_in, numstr,
1431
0
                            InvalidOid, -1,
1432
0
                            (Node *) &escontext,
1433
0
                            &datum);
1434
1435
0
          if (!noerr || escontext.error_occurred)
1436
0
            RETURN_ERROR(ereport(ERROR,
1437
0
                       (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1438
0
                        errmsg("argument \"%s\" of jsonpath item method .%s() is invalid for type %s",
1439
0
                           numstr, jspOperationName(jsp->type), "numeric"))));
1440
1441
0
          num = DatumGetNumeric(datum);
1442
0
          if (numeric_is_nan(num) || numeric_is_inf(num))
1443
0
            RETURN_ERROR(ereport(ERROR,
1444
0
                       (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1445
0
                        errmsg("NaN or Infinity is not allowed for jsonpath item method .%s()",
1446
0
                           jspOperationName(jsp->type)))));
1447
1448
0
          res = jperOk;
1449
0
        }
1450
1451
0
        if (res == jperNotFound)
1452
0
          RETURN_ERROR(ereport(ERROR,
1453
0
                     (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1454
0
                      errmsg("jsonpath item method .%s() can only be applied to a string or numeric value",
1455
0
                         jspOperationName(jsp->type)))));
1456
1457
        /*
1458
         * If we have arguments, then they must be the precision and
1459
         * optional scale used in .decimal().  Convert them to the
1460
         * typmod equivalent and then truncate the numeric value per
1461
         * this typmod details.
1462
         */
1463
0
        if (jsp->type == jpiDecimal && jsp->content.args.left)
1464
0
        {
1465
0
          Datum   numdatum;
1466
0
          Datum   dtypmod;
1467
0
          int32   precision;
1468
0
          int32   scale = 0;
1469
0
          bool    have_error;
1470
0
          bool    noerr;
1471
0
          ArrayType  *arrtypmod;
1472
0
          Datum   datums[2];
1473
0
          char    pstr[12]; /* sign, 10 digits and '\0' */
1474
0
          char    sstr[12]; /* sign, 10 digits and '\0' */
1475
0
          ErrorSaveContext escontext = {T_ErrorSaveContext};
1476
1477
0
          jspGetLeftArg(jsp, &elem);
1478
0
          if (elem.type != jpiNumeric)
1479
0
            elog(ERROR, "invalid jsonpath item type for .decimal() precision");
1480
1481
0
          precision = numeric_int4_opt_error(jspGetNumeric(&elem),
1482
0
                             &have_error);
1483
0
          if (have_error)
1484
0
            RETURN_ERROR(ereport(ERROR,
1485
0
                       (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1486
0
                        errmsg("precision of jsonpath item method .%s() is out of range for type integer",
1487
0
                           jspOperationName(jsp->type)))));
1488
1489
0
          if (jsp->content.args.right)
1490
0
          {
1491
0
            jspGetRightArg(jsp, &elem);
1492
0
            if (elem.type != jpiNumeric)
1493
0
              elog(ERROR, "invalid jsonpath item type for .decimal() scale");
1494
1495
0
            scale = numeric_int4_opt_error(jspGetNumeric(&elem),
1496
0
                             &have_error);
1497
0
            if (have_error)
1498
0
              RETURN_ERROR(ereport(ERROR,
1499
0
                         (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1500
0
                          errmsg("scale of jsonpath item method .%s() is out of range for type integer",
1501
0
                             jspOperationName(jsp->type)))));
1502
0
          }
1503
1504
          /*
1505
           * numerictypmodin() takes the precision and scale in the
1506
           * form of CString arrays.
1507
           */
1508
0
          pg_ltoa(precision, pstr);
1509
0
          datums[0] = CStringGetDatum(pstr);
1510
0
          pg_ltoa(scale, sstr);
1511
0
          datums[1] = CStringGetDatum(sstr);
1512
0
          arrtypmod = construct_array_builtin(datums, 2, CSTRINGOID);
1513
1514
0
          dtypmod = DirectFunctionCall1(numerictypmodin,
1515
0
                          PointerGetDatum(arrtypmod));
1516
1517
          /* Convert numstr to Numeric with typmod */
1518
0
          Assert(numstr != NULL);
1519
0
          noerr = DirectInputFunctionCallSafe(numeric_in, numstr,
1520
0
                            InvalidOid, dtypmod,
1521
0
                            (Node *) &escontext,
1522
0
                            &numdatum);
1523
1524
0
          if (!noerr || escontext.error_occurred)
1525
0
            RETURN_ERROR(ereport(ERROR,
1526
0
                       (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1527
0
                        errmsg("argument \"%s\" of jsonpath item method .%s() is invalid for type %s",
1528
0
                           numstr, jspOperationName(jsp->type), "numeric"))));
1529
1530
0
          num = DatumGetNumeric(numdatum);
1531
0
          pfree(arrtypmod);
1532
0
        }
1533
1534
0
        jb = &jbv;
1535
0
        jb->type = jbvNumeric;
1536
0
        jb->val.numeric = num;
1537
1538
0
        res = executeNextItem(cxt, jsp, NULL, jb, found, true);
1539
0
      }
1540
0
      break;
1541
1542
0
    case jpiInteger:
1543
0
      {
1544
0
        JsonbValue  jbv;
1545
0
        Datum   datum;
1546
1547
0
        if (unwrap && JsonbType(jb) == jbvArray)
1548
0
          return executeItemUnwrapTargetArray(cxt, jsp, jb, found,
1549
0
                            false);
1550
1551
0
        if (jb->type == jbvNumeric)
1552
0
        {
1553
0
          bool    have_error;
1554
0
          int32   val;
1555
1556
0
          val = numeric_int4_opt_error(jb->val.numeric, &have_error);
1557
0
          if (have_error)
1558
0
            RETURN_ERROR(ereport(ERROR,
1559
0
                       (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1560
0
                        errmsg("argument \"%s\" of jsonpath item method .%s() is invalid for type %s",
1561
0
                           DatumGetCString(DirectFunctionCall1(numeric_out,
1562
0
                                             NumericGetDatum(jb->val.numeric))),
1563
0
                           jspOperationName(jsp->type), "integer"))));
1564
1565
0
          datum = Int32GetDatum(val);
1566
0
          res = jperOk;
1567
0
        }
1568
0
        else if (jb->type == jbvString)
1569
0
        {
1570
          /* cast string as integer */
1571
0
          char     *tmp = pnstrdup(jb->val.string.val,
1572
0
                         jb->val.string.len);
1573
0
          ErrorSaveContext escontext = {T_ErrorSaveContext};
1574
0
          bool    noerr;
1575
1576
0
          noerr = DirectInputFunctionCallSafe(int4in, tmp,
1577
0
                            InvalidOid, -1,
1578
0
                            (Node *) &escontext,
1579
0
                            &datum);
1580
1581
0
          if (!noerr || escontext.error_occurred)
1582
0
            RETURN_ERROR(ereport(ERROR,
1583
0
                       (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1584
0
                        errmsg("argument \"%s\" of jsonpath item method .%s() is invalid for type %s",
1585
0
                           tmp, jspOperationName(jsp->type), "integer"))));
1586
0
          res = jperOk;
1587
0
        }
1588
1589
0
        if (res == jperNotFound)
1590
0
          RETURN_ERROR(ereport(ERROR,
1591
0
                     (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1592
0
                      errmsg("jsonpath item method .%s() can only be applied to a string or numeric value",
1593
0
                         jspOperationName(jsp->type)))));
1594
1595
0
        jb = &jbv;
1596
0
        jb->type = jbvNumeric;
1597
0
        jb->val.numeric = DatumGetNumeric(DirectFunctionCall1(int4_numeric,
1598
0
                                    datum));
1599
1600
0
        res = executeNextItem(cxt, jsp, NULL, jb, found, true);
1601
0
      }
1602
0
      break;
1603
1604
0
    case jpiStringFunc:
1605
0
      {
1606
0
        JsonbValue  jbv;
1607
0
        char     *tmp = NULL;
1608
1609
0
        if (unwrap && JsonbType(jb) == jbvArray)
1610
0
          return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
1611
1612
0
        switch (JsonbType(jb))
1613
0
        {
1614
0
          case jbvString:
1615
1616
            /*
1617
             * Value is not necessarily null-terminated, so we do
1618
             * pnstrdup() here.
1619
             */
1620
0
            tmp = pnstrdup(jb->val.string.val,
1621
0
                     jb->val.string.len);
1622
0
            break;
1623
0
          case jbvNumeric:
1624
0
            tmp = DatumGetCString(DirectFunctionCall1(numeric_out,
1625
0
                                  NumericGetDatum(jb->val.numeric)));
1626
0
            break;
1627
0
          case jbvBool:
1628
0
            tmp = (jb->val.boolean) ? "true" : "false";
1629
0
            break;
1630
0
          case jbvDatetime:
1631
0
            {
1632
0
              char    buf[MAXDATELEN + 1];
1633
1634
0
              JsonEncodeDateTime(buf,
1635
0
                         jb->val.datetime.value,
1636
0
                         jb->val.datetime.typid,
1637
0
                         &jb->val.datetime.tz);
1638
0
              tmp = pstrdup(buf);
1639
0
            }
1640
0
            break;
1641
0
          case jbvNull:
1642
0
          case jbvArray:
1643
0
          case jbvObject:
1644
0
          case jbvBinary:
1645
0
            RETURN_ERROR(ereport(ERROR,
1646
0
                       (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1647
0
                        errmsg("jsonpath item method .%s() can only be applied to a boolean, string, numeric, or datetime value",
1648
0
                           jspOperationName(jsp->type)))));
1649
0
            break;
1650
0
        }
1651
1652
0
        jb = &jbv;
1653
0
        Assert(tmp != NULL);  /* We must have set tmp above */
1654
0
        jb->val.string.val = tmp;
1655
0
        jb->val.string.len = strlen(jb->val.string.val);
1656
0
        jb->type = jbvString;
1657
1658
0
        res = executeNextItem(cxt, jsp, NULL, jb, found, true);
1659
0
      }
1660
0
      break;
1661
1662
0
    default:
1663
0
      elog(ERROR, "unrecognized jsonpath item type: %d", jsp->type);
1664
0
  }
1665
1666
0
  return res;
1667
0
}
1668
1669
/*
1670
 * Unwrap current array item and execute jsonpath for each of its elements.
1671
 */
1672
static JsonPathExecResult
1673
executeItemUnwrapTargetArray(JsonPathExecContext *cxt, JsonPathItem *jsp,
1674
               JsonbValue *jb, JsonValueList *found,
1675
               bool unwrapElements)
1676
0
{
1677
0
  if (jb->type != jbvBinary)
1678
0
  {
1679
0
    Assert(jb->type != jbvArray);
1680
0
    elog(ERROR, "invalid jsonb array value type: %d", jb->type);
1681
0
  }
1682
1683
0
  return executeAnyItem
1684
0
    (cxt, jsp, jb->val.binary.data, found, 1, 1, 1,
1685
0
     false, unwrapElements);
1686
0
}
1687
1688
/*
1689
 * Execute next jsonpath item if exists.  Otherwise put "v" to the "found"
1690
 * list if provided.
1691
 */
1692
static JsonPathExecResult
1693
executeNextItem(JsonPathExecContext *cxt,
1694
        JsonPathItem *cur, JsonPathItem *next,
1695
        JsonbValue *v, JsonValueList *found, bool copy)
1696
0
{
1697
0
  JsonPathItem elem;
1698
0
  bool    hasNext;
1699
1700
0
  if (!cur)
1701
0
    hasNext = next != NULL;
1702
0
  else if (next)
1703
0
    hasNext = jspHasNext(cur);
1704
0
  else
1705
0
  {
1706
0
    next = &elem;
1707
0
    hasNext = jspGetNext(cur, next);
1708
0
  }
1709
1710
0
  if (hasNext)
1711
0
    return executeItem(cxt, next, v, found);
1712
1713
0
  if (found)
1714
0
    JsonValueListAppend(found, copy ? copyJsonbValue(v) : v);
1715
1716
0
  return jperOk;
1717
0
}
1718
1719
/*
1720
 * Same as executeItem(), but when "unwrap == true" automatically unwraps
1721
 * each array item from the resulting sequence in lax mode.
1722
 */
1723
static JsonPathExecResult
1724
executeItemOptUnwrapResult(JsonPathExecContext *cxt, JsonPathItem *jsp,
1725
               JsonbValue *jb, bool unwrap,
1726
               JsonValueList *found)
1727
0
{
1728
0
  if (unwrap && jspAutoUnwrap(cxt))
1729
0
  {
1730
0
    JsonValueList seq = {0};
1731
0
    JsonValueListIterator it;
1732
0
    JsonPathExecResult res = executeItem(cxt, jsp, jb, &seq);
1733
0
    JsonbValue *item;
1734
1735
0
    if (jperIsError(res))
1736
0
      return res;
1737
1738
0
    JsonValueListInitIterator(&seq, &it);
1739
0
    while ((item = JsonValueListNext(&seq, &it)))
1740
0
    {
1741
0
      Assert(item->type != jbvArray);
1742
1743
0
      if (JsonbType(item) == jbvArray)
1744
0
        executeItemUnwrapTargetArray(cxt, NULL, item, found, false);
1745
0
      else
1746
0
        JsonValueListAppend(found, item);
1747
0
    }
1748
1749
0
    return jperOk;
1750
0
  }
1751
1752
0
  return executeItem(cxt, jsp, jb, found);
1753
0
}
1754
1755
/*
1756
 * Same as executeItemOptUnwrapResult(), but with error suppression.
1757
 */
1758
static JsonPathExecResult
1759
executeItemOptUnwrapResultNoThrow(JsonPathExecContext *cxt,
1760
                  JsonPathItem *jsp,
1761
                  JsonbValue *jb, bool unwrap,
1762
                  JsonValueList *found)
1763
0
{
1764
0
  JsonPathExecResult res;
1765
0
  bool    throwErrors = cxt->throwErrors;
1766
1767
0
  cxt->throwErrors = false;
1768
0
  res = executeItemOptUnwrapResult(cxt, jsp, jb, unwrap, found);
1769
0
  cxt->throwErrors = throwErrors;
1770
1771
0
  return res;
1772
0
}
1773
1774
/* Execute boolean-valued jsonpath expression. */
1775
static JsonPathBool
1776
executeBoolItem(JsonPathExecContext *cxt, JsonPathItem *jsp,
1777
        JsonbValue *jb, bool canHaveNext)
1778
0
{
1779
0
  JsonPathItem larg;
1780
0
  JsonPathItem rarg;
1781
0
  JsonPathBool res;
1782
0
  JsonPathBool res2;
1783
1784
  /* since this function recurses, it could be driven to stack overflow */
1785
0
  check_stack_depth();
1786
1787
0
  if (!canHaveNext && jspHasNext(jsp))
1788
0
    elog(ERROR, "boolean jsonpath item cannot have next item");
1789
1790
0
  switch (jsp->type)
1791
0
  {
1792
0
    case jpiAnd:
1793
0
      jspGetLeftArg(jsp, &larg);
1794
0
      res = executeBoolItem(cxt, &larg, jb, false);
1795
1796
0
      if (res == jpbFalse)
1797
0
        return jpbFalse;
1798
1799
      /*
1800
       * SQL/JSON says that we should check second arg in case of
1801
       * jperError
1802
       */
1803
1804
0
      jspGetRightArg(jsp, &rarg);
1805
0
      res2 = executeBoolItem(cxt, &rarg, jb, false);
1806
1807
0
      return res2 == jpbTrue ? res : res2;
1808
1809
0
    case jpiOr:
1810
0
      jspGetLeftArg(jsp, &larg);
1811
0
      res = executeBoolItem(cxt, &larg, jb, false);
1812
1813
0
      if (res == jpbTrue)
1814
0
        return jpbTrue;
1815
1816
0
      jspGetRightArg(jsp, &rarg);
1817
0
      res2 = executeBoolItem(cxt, &rarg, jb, false);
1818
1819
0
      return res2 == jpbFalse ? res : res2;
1820
1821
0
    case jpiNot:
1822
0
      jspGetArg(jsp, &larg);
1823
1824
0
      res = executeBoolItem(cxt, &larg, jb, false);
1825
1826
0
      if (res == jpbUnknown)
1827
0
        return jpbUnknown;
1828
1829
0
      return res == jpbTrue ? jpbFalse : jpbTrue;
1830
1831
0
    case jpiIsUnknown:
1832
0
      jspGetArg(jsp, &larg);
1833
0
      res = executeBoolItem(cxt, &larg, jb, false);
1834
0
      return res == jpbUnknown ? jpbTrue : jpbFalse;
1835
1836
0
    case jpiEqual:
1837
0
    case jpiNotEqual:
1838
0
    case jpiLess:
1839
0
    case jpiGreater:
1840
0
    case jpiLessOrEqual:
1841
0
    case jpiGreaterOrEqual:
1842
0
      jspGetLeftArg(jsp, &larg);
1843
0
      jspGetRightArg(jsp, &rarg);
1844
0
      return executePredicate(cxt, jsp, &larg, &rarg, jb, true,
1845
0
                  executeComparison, cxt);
1846
1847
0
    case jpiStartsWith:   /* 'whole STARTS WITH initial' */
1848
0
      jspGetLeftArg(jsp, &larg);  /* 'whole' */
1849
0
      jspGetRightArg(jsp, &rarg); /* 'initial' */
1850
0
      return executePredicate(cxt, jsp, &larg, &rarg, jb, false,
1851
0
                  executeStartsWith, NULL);
1852
1853
0
    case jpiLikeRegex:    /* 'expr LIKE_REGEX pattern FLAGS flags' */
1854
0
      {
1855
        /*
1856
         * 'expr' is a sequence-returning expression.  'pattern' is a
1857
         * regex string literal.  SQL/JSON standard requires XQuery
1858
         * regexes, but we use Postgres regexes here.  'flags' is a
1859
         * string literal converted to integer flags at compile-time.
1860
         */
1861
0
        JsonLikeRegexContext lrcxt = {0};
1862
1863
0
        jspInitByBuffer(&larg, jsp->base,
1864
0
                jsp->content.like_regex.expr);
1865
1866
0
        return executePredicate(cxt, jsp, &larg, NULL, jb, false,
1867
0
                    executeLikeRegex, &lrcxt);
1868
0
      }
1869
1870
0
    case jpiExists:
1871
0
      jspGetArg(jsp, &larg);
1872
1873
0
      if (jspStrictAbsenceOfErrors(cxt))
1874
0
      {
1875
        /*
1876
         * In strict mode we must get a complete list of values to
1877
         * check that there are no errors at all.
1878
         */
1879
0
        JsonValueList vals = {0};
1880
0
        JsonPathExecResult res =
1881
0
          executeItemOptUnwrapResultNoThrow(cxt, &larg, jb,
1882
0
                            false, &vals);
1883
1884
0
        if (jperIsError(res))
1885
0
          return jpbUnknown;
1886
1887
0
        return JsonValueListIsEmpty(&vals) ? jpbFalse : jpbTrue;
1888
0
      }
1889
0
      else
1890
0
      {
1891
0
        JsonPathExecResult res =
1892
0
          executeItemOptUnwrapResultNoThrow(cxt, &larg, jb,
1893
0
                            false, NULL);
1894
1895
0
        if (jperIsError(res))
1896
0
          return jpbUnknown;
1897
1898
0
        return res == jperOk ? jpbTrue : jpbFalse;
1899
0
      }
1900
1901
0
    default:
1902
0
      elog(ERROR, "invalid boolean jsonpath item type: %d", jsp->type);
1903
0
      return jpbUnknown;
1904
0
  }
1905
0
}
1906
1907
/*
1908
 * Execute nested (filters etc.) boolean expression pushing current SQL/JSON
1909
 * item onto the stack.
1910
 */
1911
static JsonPathBool
1912
executeNestedBoolItem(JsonPathExecContext *cxt, JsonPathItem *jsp,
1913
            JsonbValue *jb)
1914
0
{
1915
0
  JsonbValue *prev;
1916
0
  JsonPathBool res;
1917
1918
0
  prev = cxt->current;
1919
0
  cxt->current = jb;
1920
0
  res = executeBoolItem(cxt, jsp, jb, false);
1921
0
  cxt->current = prev;
1922
1923
0
  return res;
1924
0
}
1925
1926
/*
1927
 * Implementation of several jsonpath nodes:
1928
 *  - jpiAny (.** accessor),
1929
 *  - jpiAnyKey (.* accessor),
1930
 *  - jpiAnyArray ([*] accessor)
1931
 */
1932
static JsonPathExecResult
1933
executeAnyItem(JsonPathExecContext *cxt, JsonPathItem *jsp, JsonbContainer *jbc,
1934
         JsonValueList *found, uint32 level, uint32 first, uint32 last,
1935
         bool ignoreStructuralErrors, bool unwrapNext)
1936
0
{
1937
0
  JsonPathExecResult res = jperNotFound;
1938
0
  JsonbIterator *it;
1939
0
  int32   r;
1940
0
  JsonbValue  v;
1941
1942
0
  check_stack_depth();
1943
1944
0
  if (level > last)
1945
0
    return res;
1946
1947
0
  it = JsonbIteratorInit(jbc);
1948
1949
  /*
1950
   * Recursively iterate over jsonb objects/arrays
1951
   */
1952
0
  while ((r = JsonbIteratorNext(&it, &v, true)) != WJB_DONE)
1953
0
  {
1954
0
    if (r == WJB_KEY)
1955
0
    {
1956
0
      r = JsonbIteratorNext(&it, &v, true);
1957
0
      Assert(r == WJB_VALUE);
1958
0
    }
1959
1960
0
    if (r == WJB_VALUE || r == WJB_ELEM)
1961
0
    {
1962
1963
0
      if (level >= first ||
1964
0
        (first == PG_UINT32_MAX && last == PG_UINT32_MAX &&
1965
0
         v.type != jbvBinary))  /* leaves only requested */
1966
0
      {
1967
        /* check expression */
1968
0
        if (jsp)
1969
0
        {
1970
0
          if (ignoreStructuralErrors)
1971
0
          {
1972
0
            bool    savedIgnoreStructuralErrors;
1973
1974
0
            savedIgnoreStructuralErrors = cxt->ignoreStructuralErrors;
1975
0
            cxt->ignoreStructuralErrors = true;
1976
0
            res = executeItemOptUnwrapTarget(cxt, jsp, &v, found, unwrapNext);
1977
0
            cxt->ignoreStructuralErrors = savedIgnoreStructuralErrors;
1978
0
          }
1979
0
          else
1980
0
            res = executeItemOptUnwrapTarget(cxt, jsp, &v, found, unwrapNext);
1981
1982
0
          if (jperIsError(res))
1983
0
            break;
1984
1985
0
          if (res == jperOk && !found)
1986
0
            break;
1987
0
        }
1988
0
        else if (found)
1989
0
          JsonValueListAppend(found, copyJsonbValue(&v));
1990
0
        else
1991
0
          return jperOk;
1992
0
      }
1993
1994
0
      if (level < last && v.type == jbvBinary)
1995
0
      {
1996
0
        res = executeAnyItem
1997
0
          (cxt, jsp, v.val.binary.data, found,
1998
0
           level + 1, first, last,
1999
0
           ignoreStructuralErrors, unwrapNext);
2000
2001
0
        if (jperIsError(res))
2002
0
          break;
2003
2004
0
        if (res == jperOk && found == NULL)
2005
0
          break;
2006
0
      }
2007
0
    }
2008
0
  }
2009
2010
0
  return res;
2011
0
}
2012
2013
/*
2014
 * Execute unary or binary predicate.
2015
 *
2016
 * Predicates have existence semantics, because their operands are item
2017
 * sequences.  Pairs of items from the left and right operand's sequences are
2018
 * checked.  TRUE returned only if any pair satisfying the condition is found.
2019
 * In strict mode, even if the desired pair has already been found, all pairs
2020
 * still need to be examined to check the absence of errors.  If any error
2021
 * occurs, UNKNOWN (analogous to SQL NULL) is returned.
2022
 */
2023
static JsonPathBool
2024
executePredicate(JsonPathExecContext *cxt, JsonPathItem *pred,
2025
         JsonPathItem *larg, JsonPathItem *rarg, JsonbValue *jb,
2026
         bool unwrapRightArg, JsonPathPredicateCallback exec,
2027
         void *param)
2028
0
{
2029
0
  JsonPathExecResult res;
2030
0
  JsonValueListIterator lseqit;
2031
0
  JsonValueList lseq = {0};
2032
0
  JsonValueList rseq = {0};
2033
0
  JsonbValue *lval;
2034
0
  bool    error = false;
2035
0
  bool    found = false;
2036
2037
  /* Left argument is always auto-unwrapped. */
2038
0
  res = executeItemOptUnwrapResultNoThrow(cxt, larg, jb, true, &lseq);
2039
0
  if (jperIsError(res))
2040
0
    return jpbUnknown;
2041
2042
0
  if (rarg)
2043
0
  {
2044
    /* Right argument is conditionally auto-unwrapped. */
2045
0
    res = executeItemOptUnwrapResultNoThrow(cxt, rarg, jb,
2046
0
                        unwrapRightArg, &rseq);
2047
0
    if (jperIsError(res))
2048
0
      return jpbUnknown;
2049
0
  }
2050
2051
0
  JsonValueListInitIterator(&lseq, &lseqit);
2052
0
  while ((lval = JsonValueListNext(&lseq, &lseqit)))
2053
0
  {
2054
0
    JsonValueListIterator rseqit;
2055
0
    JsonbValue *rval;
2056
0
    bool    first = true;
2057
2058
0
    JsonValueListInitIterator(&rseq, &rseqit);
2059
0
    if (rarg)
2060
0
      rval = JsonValueListNext(&rseq, &rseqit);
2061
0
    else
2062
0
      rval = NULL;
2063
2064
    /* Loop over right arg sequence or do single pass otherwise */
2065
0
    while (rarg ? (rval != NULL) : first)
2066
0
    {
2067
0
      JsonPathBool res = exec(pred, lval, rval, param);
2068
2069
0
      if (res == jpbUnknown)
2070
0
      {
2071
0
        if (jspStrictAbsenceOfErrors(cxt))
2072
0
          return jpbUnknown;
2073
2074
0
        error = true;
2075
0
      }
2076
0
      else if (res == jpbTrue)
2077
0
      {
2078
0
        if (!jspStrictAbsenceOfErrors(cxt))
2079
0
          return jpbTrue;
2080
2081
0
        found = true;
2082
0
      }
2083
2084
0
      first = false;
2085
0
      if (rarg)
2086
0
        rval = JsonValueListNext(&rseq, &rseqit);
2087
0
    }
2088
0
  }
2089
2090
0
  if (found)         /* possible only in strict mode */
2091
0
    return jpbTrue;
2092
2093
0
  if (error)         /* possible only in lax mode */
2094
0
    return jpbUnknown;
2095
2096
0
  return jpbFalse;
2097
0
}
2098
2099
/*
2100
 * Execute binary arithmetic expression on singleton numeric operands.
2101
 * Array operands are automatically unwrapped in lax mode.
2102
 */
2103
static JsonPathExecResult
2104
executeBinaryArithmExpr(JsonPathExecContext *cxt, JsonPathItem *jsp,
2105
            JsonbValue *jb, BinaryArithmFunc func,
2106
            JsonValueList *found)
2107
0
{
2108
0
  JsonPathExecResult jper;
2109
0
  JsonPathItem elem;
2110
0
  JsonValueList lseq = {0};
2111
0
  JsonValueList rseq = {0};
2112
0
  JsonbValue *lval;
2113
0
  JsonbValue *rval;
2114
0
  Numeric   res;
2115
2116
0
  jspGetLeftArg(jsp, &elem);
2117
2118
  /*
2119
   * XXX: By standard only operands of multiplicative expressions are
2120
   * unwrapped.  We extend it to other binary arithmetic expressions too.
2121
   */
2122
0
  jper = executeItemOptUnwrapResult(cxt, &elem, jb, true, &lseq);
2123
0
  if (jperIsError(jper))
2124
0
    return jper;
2125
2126
0
  jspGetRightArg(jsp, &elem);
2127
2128
0
  jper = executeItemOptUnwrapResult(cxt, &elem, jb, true, &rseq);
2129
0
  if (jperIsError(jper))
2130
0
    return jper;
2131
2132
0
  if (JsonValueListLength(&lseq) != 1 ||
2133
0
    !(lval = getScalar(JsonValueListHead(&lseq), jbvNumeric)))
2134
0
    RETURN_ERROR(ereport(ERROR,
2135
0
               (errcode(ERRCODE_SINGLETON_SQL_JSON_ITEM_REQUIRED),
2136
0
                errmsg("left operand of jsonpath operator %s is not a single numeric value",
2137
0
                   jspOperationName(jsp->type)))));
2138
2139
0
  if (JsonValueListLength(&rseq) != 1 ||
2140
0
    !(rval = getScalar(JsonValueListHead(&rseq), jbvNumeric)))
2141
0
    RETURN_ERROR(ereport(ERROR,
2142
0
               (errcode(ERRCODE_SINGLETON_SQL_JSON_ITEM_REQUIRED),
2143
0
                errmsg("right operand of jsonpath operator %s is not a single numeric value",
2144
0
                   jspOperationName(jsp->type)))));
2145
2146
0
  if (jspThrowErrors(cxt))
2147
0
  {
2148
0
    res = func(lval->val.numeric, rval->val.numeric, NULL);
2149
0
  }
2150
0
  else
2151
0
  {
2152
0
    bool    error = false;
2153
2154
0
    res = func(lval->val.numeric, rval->val.numeric, &error);
2155
2156
0
    if (error)
2157
0
      return jperError;
2158
0
  }
2159
2160
0
  if (!jspGetNext(jsp, &elem) && !found)
2161
0
    return jperOk;
2162
2163
0
  lval = palloc(sizeof(*lval));
2164
0
  lval->type = jbvNumeric;
2165
0
  lval->val.numeric = res;
2166
2167
0
  return executeNextItem(cxt, jsp, &elem, lval, found, false);
2168
0
}
2169
2170
/*
2171
 * Execute unary arithmetic expression for each numeric item in its operand's
2172
 * sequence.  Array operand is automatically unwrapped in lax mode.
2173
 */
2174
static JsonPathExecResult
2175
executeUnaryArithmExpr(JsonPathExecContext *cxt, JsonPathItem *jsp,
2176
             JsonbValue *jb, PGFunction func, JsonValueList *found)
2177
0
{
2178
0
  JsonPathExecResult jper;
2179
0
  JsonPathExecResult jper2;
2180
0
  JsonPathItem elem;
2181
0
  JsonValueList seq = {0};
2182
0
  JsonValueListIterator it;
2183
0
  JsonbValue *val;
2184
0
  bool    hasNext;
2185
2186
0
  jspGetArg(jsp, &elem);
2187
0
  jper = executeItemOptUnwrapResult(cxt, &elem, jb, true, &seq);
2188
2189
0
  if (jperIsError(jper))
2190
0
    return jper;
2191
2192
0
  jper = jperNotFound;
2193
2194
0
  hasNext = jspGetNext(jsp, &elem);
2195
2196
0
  JsonValueListInitIterator(&seq, &it);
2197
0
  while ((val = JsonValueListNext(&seq, &it)))
2198
0
  {
2199
0
    if ((val = getScalar(val, jbvNumeric)))
2200
0
    {
2201
0
      if (!found && !hasNext)
2202
0
        return jperOk;
2203
0
    }
2204
0
    else
2205
0
    {
2206
0
      if (!found && !hasNext)
2207
0
        continue;   /* skip non-numerics processing */
2208
2209
0
      RETURN_ERROR(ereport(ERROR,
2210
0
                 (errcode(ERRCODE_SQL_JSON_NUMBER_NOT_FOUND),
2211
0
                  errmsg("operand of unary jsonpath operator %s is not a numeric value",
2212
0
                     jspOperationName(jsp->type)))));
2213
0
    }
2214
2215
0
    if (func)
2216
0
      val->val.numeric =
2217
0
        DatumGetNumeric(DirectFunctionCall1(func,
2218
0
                          NumericGetDatum(val->val.numeric)));
2219
2220
0
    jper2 = executeNextItem(cxt, jsp, &elem, val, found, false);
2221
2222
0
    if (jperIsError(jper2))
2223
0
      return jper2;
2224
2225
0
    if (jper2 == jperOk)
2226
0
    {
2227
0
      if (!found)
2228
0
        return jperOk;
2229
0
      jper = jperOk;
2230
0
    }
2231
0
  }
2232
2233
0
  return jper;
2234
0
}
2235
2236
/*
2237
 * STARTS_WITH predicate callback.
2238
 *
2239
 * Check if the 'whole' string starts from 'initial' string.
2240
 */
2241
static JsonPathBool
2242
executeStartsWith(JsonPathItem *jsp, JsonbValue *whole, JsonbValue *initial,
2243
          void *param)
2244
0
{
2245
0
  if (!(whole = getScalar(whole, jbvString)))
2246
0
    return jpbUnknown;   /* error */
2247
2248
0
  if (!(initial = getScalar(initial, jbvString)))
2249
0
    return jpbUnknown;   /* error */
2250
2251
0
  if (whole->val.string.len >= initial->val.string.len &&
2252
0
    !memcmp(whole->val.string.val,
2253
0
        initial->val.string.val,
2254
0
        initial->val.string.len))
2255
0
    return jpbTrue;
2256
2257
0
  return jpbFalse;
2258
0
}
2259
2260
/*
2261
 * LIKE_REGEX predicate callback.
2262
 *
2263
 * Check if the string matches regex pattern.
2264
 */
2265
static JsonPathBool
2266
executeLikeRegex(JsonPathItem *jsp, JsonbValue *str, JsonbValue *rarg,
2267
         void *param)
2268
0
{
2269
0
  JsonLikeRegexContext *cxt = param;
2270
2271
0
  if (!(str = getScalar(str, jbvString)))
2272
0
    return jpbUnknown;
2273
2274
  /* Cache regex text and converted flags. */
2275
0
  if (!cxt->regex)
2276
0
  {
2277
0
    cxt->regex =
2278
0
      cstring_to_text_with_len(jsp->content.like_regex.pattern,
2279
0
                   jsp->content.like_regex.patternlen);
2280
0
    (void) jspConvertRegexFlags(jsp->content.like_regex.flags,
2281
0
                  &(cxt->cflags), NULL);
2282
0
  }
2283
2284
0
  if (RE_compile_and_execute(cxt->regex, str->val.string.val,
2285
0
                 str->val.string.len,
2286
0
                 cxt->cflags, DEFAULT_COLLATION_OID, 0, NULL))
2287
0
    return jpbTrue;
2288
2289
0
  return jpbFalse;
2290
0
}
2291
2292
/*
2293
 * Execute numeric item methods (.abs(), .floor(), .ceil()) using the specified
2294
 * user function 'func'.
2295
 */
2296
static JsonPathExecResult
2297
executeNumericItemMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
2298
             JsonbValue *jb, bool unwrap, PGFunction func,
2299
             JsonValueList *found)
2300
0
{
2301
0
  JsonPathItem next;
2302
0
  Datum   datum;
2303
2304
0
  if (unwrap && JsonbType(jb) == jbvArray)
2305
0
    return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
2306
2307
0
  if (!(jb = getScalar(jb, jbvNumeric)))
2308
0
    RETURN_ERROR(ereport(ERROR,
2309
0
               (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
2310
0
                errmsg("jsonpath item method .%s() can only be applied to a numeric value",
2311
0
                   jspOperationName(jsp->type)))));
2312
2313
0
  datum = DirectFunctionCall1(func, NumericGetDatum(jb->val.numeric));
2314
2315
0
  if (!jspGetNext(jsp, &next) && !found)
2316
0
    return jperOk;
2317
2318
0
  jb = palloc(sizeof(*jb));
2319
0
  jb->type = jbvNumeric;
2320
0
  jb->val.numeric = DatumGetNumeric(datum);
2321
2322
0
  return executeNextItem(cxt, jsp, &next, jb, found, false);
2323
0
}
2324
2325
/*
2326
 * Implementation of the .datetime() and related methods.
2327
 *
2328
 * Converts a string into a date/time value. The actual type is determined at
2329
 * run time.
2330
 * If an argument is provided, this argument is used as a template string.
2331
 * Otherwise, the first fitting ISO format is selected.
2332
 *
2333
 * .date(), .time(), .time_tz(), .timestamp(), .timestamp_tz() methods don't
2334
 * have a format, so ISO format is used.  However, except for .date(), they all
2335
 * take an optional time precision.
2336
 */
2337
static JsonPathExecResult
2338
executeDateTimeMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
2339
            JsonbValue *jb, JsonValueList *found)
2340
0
{
2341
0
  JsonbValue  jbvbuf;
2342
0
  Datum   value;
2343
0
  text     *datetime;
2344
0
  Oid     collid;
2345
0
  Oid     typid;
2346
0
  int32   typmod = -1;
2347
0
  int     tz = 0;
2348
0
  bool    hasNext;
2349
0
  JsonPathExecResult res = jperNotFound;
2350
0
  JsonPathItem elem;
2351
0
  int32   time_precision = -1;
2352
2353
0
  if (!(jb = getScalar(jb, jbvString)))
2354
0
    RETURN_ERROR(ereport(ERROR,
2355
0
               (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
2356
0
                errmsg("jsonpath item method .%s() can only be applied to a string",
2357
0
                   jspOperationName(jsp->type)))));
2358
2359
0
  datetime = cstring_to_text_with_len(jb->val.string.val,
2360
0
                    jb->val.string.len);
2361
2362
  /*
2363
   * At some point we might wish to have callers supply the collation to
2364
   * use, but right now it's unclear that they'd be able to do better than
2365
   * DEFAULT_COLLATION_OID anyway.
2366
   */
2367
0
  collid = DEFAULT_COLLATION_OID;
2368
2369
  /*
2370
   * .datetime(template) has an argument, the rest of the methods don't have
2371
   * an argument.  So we handle that separately.
2372
   */
2373
0
  if (jsp->type == jpiDatetime && jsp->content.arg)
2374
0
  {
2375
0
    text     *template;
2376
0
    char     *template_str;
2377
0
    int     template_len;
2378
0
    ErrorSaveContext escontext = {T_ErrorSaveContext};
2379
2380
0
    jspGetArg(jsp, &elem);
2381
2382
0
    if (elem.type != jpiString)
2383
0
      elog(ERROR, "invalid jsonpath item type for .datetime() argument");
2384
2385
0
    template_str = jspGetString(&elem, &template_len);
2386
2387
0
    template = cstring_to_text_with_len(template_str,
2388
0
                      template_len);
2389
2390
0
    value = parse_datetime(datetime, template, collid, true,
2391
0
                 &typid, &typmod, &tz,
2392
0
                 jspThrowErrors(cxt) ? NULL : (Node *) &escontext);
2393
2394
0
    if (escontext.error_occurred)
2395
0
      res = jperError;
2396
0
    else
2397
0
      res = jperOk;
2398
0
  }
2399
0
  else
2400
0
  {
2401
    /*
2402
     * According to SQL/JSON standard enumerate ISO formats for: date,
2403
     * timetz, time, timestamptz, timestamp.
2404
     *
2405
     * We also support ISO 8601 format (with "T") for timestamps, because
2406
     * to_json[b]() functions use this format.
2407
     */
2408
0
    static const char *fmt_str[] =
2409
0
    {
2410
0
      "yyyy-mm-dd",   /* date */
2411
0
      "HH24:MI:SS.USTZ",  /* timetz */
2412
0
      "HH24:MI:SSTZ",
2413
0
      "HH24:MI:SS.US",  /* time without tz */
2414
0
      "HH24:MI:SS",
2415
0
      "yyyy-mm-dd HH24:MI:SS.USTZ", /* timestamptz */
2416
0
      "yyyy-mm-dd HH24:MI:SSTZ",
2417
0
      "yyyy-mm-dd\"T\"HH24:MI:SS.USTZ",
2418
0
      "yyyy-mm-dd\"T\"HH24:MI:SSTZ",
2419
0
      "yyyy-mm-dd HH24:MI:SS.US", /* timestamp without tz */
2420
0
      "yyyy-mm-dd HH24:MI:SS",
2421
0
      "yyyy-mm-dd\"T\"HH24:MI:SS.US",
2422
0
      "yyyy-mm-dd\"T\"HH24:MI:SS"
2423
0
    };
2424
2425
    /* cache for format texts */
2426
0
    static text *fmt_txt[lengthof(fmt_str)] = {0};
2427
0
    int     i;
2428
2429
    /*
2430
     * Check for optional precision for methods other than .datetime() and
2431
     * .date()
2432
     */
2433
0
    if (jsp->type != jpiDatetime && jsp->type != jpiDate &&
2434
0
      jsp->content.arg)
2435
0
    {
2436
0
      bool    have_error;
2437
2438
0
      jspGetArg(jsp, &elem);
2439
2440
0
      if (elem.type != jpiNumeric)
2441
0
        elog(ERROR, "invalid jsonpath item type for %s argument",
2442
0
           jspOperationName(jsp->type));
2443
2444
0
      time_precision = numeric_int4_opt_error(jspGetNumeric(&elem),
2445
0
                          &have_error);
2446
0
      if (have_error)
2447
0
        RETURN_ERROR(ereport(ERROR,
2448
0
                   (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
2449
0
                    errmsg("time precision of jsonpath item method .%s() is out of range for type integer",
2450
0
                       jspOperationName(jsp->type)))));
2451
0
    }
2452
2453
    /* loop until datetime format fits */
2454
0
    for (i = 0; i < lengthof(fmt_str); i++)
2455
0
    {
2456
0
      ErrorSaveContext escontext = {T_ErrorSaveContext};
2457
2458
0
      if (!fmt_txt[i])
2459
0
      {
2460
0
        MemoryContext oldcxt =
2461
0
          MemoryContextSwitchTo(TopMemoryContext);
2462
2463
0
        fmt_txt[i] = cstring_to_text(fmt_str[i]);
2464
0
        MemoryContextSwitchTo(oldcxt);
2465
0
      }
2466
2467
0
      value = parse_datetime(datetime, fmt_txt[i], collid, true,
2468
0
                   &typid, &typmod, &tz,
2469
0
                   (Node *) &escontext);
2470
2471
0
      if (!escontext.error_occurred)
2472
0
      {
2473
0
        res = jperOk;
2474
0
        break;
2475
0
      }
2476
0
    }
2477
2478
0
    if (res == jperNotFound)
2479
0
    {
2480
0
      if (jsp->type == jpiDatetime)
2481
0
        RETURN_ERROR(ereport(ERROR,
2482
0
                   (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
2483
0
                    errmsg("%s format is not recognized: \"%s\"",
2484
0
                       "datetime", text_to_cstring(datetime)),
2485
0
                    errhint("Use a datetime template argument to specify the input data format."))));
2486
0
      else
2487
0
        RETURN_ERROR(ereport(ERROR,
2488
0
                   (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
2489
0
                    errmsg("%s format is not recognized: \"%s\"",
2490
0
                       jspOperationName(jsp->type), text_to_cstring(datetime)))));
2491
2492
0
    }
2493
0
  }
2494
2495
  /*
2496
   * parse_datetime() processes the entire input string per the template or
2497
   * ISO format and returns the Datum in best fitted datetime type.  So, if
2498
   * this call is for a specific datatype, then we do the conversion here.
2499
   * Throw an error for incompatible types.
2500
   */
2501
0
  switch (jsp->type)
2502
0
  {
2503
0
    case jpiDatetime:   /* Nothing to do for DATETIME */
2504
0
      break;
2505
0
    case jpiDate:
2506
0
      {
2507
        /* Convert result type to date */
2508
0
        switch (typid)
2509
0
        {
2510
0
          case DATEOID: /* Nothing to do for DATE */
2511
0
            break;
2512
0
          case TIMEOID:
2513
0
          case TIMETZOID:
2514
0
            RETURN_ERROR(ereport(ERROR,
2515
0
                       (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
2516
0
                        errmsg("%s format is not recognized: \"%s\"",
2517
0
                           "date", text_to_cstring(datetime)))));
2518
0
            break;
2519
0
          case TIMESTAMPOID:
2520
0
            value = DirectFunctionCall1(timestamp_date,
2521
0
                          value);
2522
0
            break;
2523
0
          case TIMESTAMPTZOID:
2524
0
            checkTimezoneIsUsedForCast(cxt->useTz,
2525
0
                           "timestamptz", "date");
2526
0
            value = DirectFunctionCall1(timestamptz_date,
2527
0
                          value);
2528
0
            break;
2529
0
          default:
2530
0
            elog(ERROR, "type with oid %u not supported", typid);
2531
0
        }
2532
2533
0
        typid = DATEOID;
2534
0
      }
2535
0
      break;
2536
0
    case jpiTime:
2537
0
      {
2538
        /* Convert result type to time without time zone */
2539
0
        switch (typid)
2540
0
        {
2541
0
          case DATEOID:
2542
0
            RETURN_ERROR(ereport(ERROR,
2543
0
                       (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
2544
0
                        errmsg("%s format is not recognized: \"%s\"",
2545
0
                           "time", text_to_cstring(datetime)))));
2546
0
            break;
2547
0
          case TIMEOID: /* Nothing to do for TIME */
2548
0
            break;
2549
0
          case TIMETZOID:
2550
0
            checkTimezoneIsUsedForCast(cxt->useTz,
2551
0
                           "timetz", "time");
2552
0
            value = DirectFunctionCall1(timetz_time,
2553
0
                          value);
2554
0
            break;
2555
0
          case TIMESTAMPOID:
2556
0
            value = DirectFunctionCall1(timestamp_time,
2557
0
                          value);
2558
0
            break;
2559
0
          case TIMESTAMPTZOID:
2560
0
            checkTimezoneIsUsedForCast(cxt->useTz,
2561
0
                           "timestamptz", "time");
2562
0
            value = DirectFunctionCall1(timestamptz_time,
2563
0
                          value);
2564
0
            break;
2565
0
          default:
2566
0
            elog(ERROR, "type with oid %u not supported", typid);
2567
0
        }
2568
2569
        /* Force the user-given time precision, if any */
2570
0
        if (time_precision != -1)
2571
0
        {
2572
0
          TimeADT   result;
2573
2574
          /* Get a warning when precision is reduced */
2575
0
          time_precision = anytime_typmod_check(false,
2576
0
                              time_precision);
2577
0
          result = DatumGetTimeADT(value);
2578
0
          AdjustTimeForTypmod(&result, time_precision);
2579
0
          value = TimeADTGetDatum(result);
2580
2581
          /* Update the typmod value with the user-given precision */
2582
0
          typmod = time_precision;
2583
0
        }
2584
2585
0
        typid = TIMEOID;
2586
0
      }
2587
0
      break;
2588
0
    case jpiTimeTz:
2589
0
      {
2590
        /* Convert result type to time with time zone */
2591
0
        switch (typid)
2592
0
        {
2593
0
          case DATEOID:
2594
0
          case TIMESTAMPOID:
2595
0
            RETURN_ERROR(ereport(ERROR,
2596
0
                       (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
2597
0
                        errmsg("%s format is not recognized: \"%s\"",
2598
0
                           "time_tz", text_to_cstring(datetime)))));
2599
0
            break;
2600
0
          case TIMEOID:
2601
0
            checkTimezoneIsUsedForCast(cxt->useTz,
2602
0
                           "time", "timetz");
2603
0
            value = DirectFunctionCall1(time_timetz,
2604
0
                          value);
2605
0
            break;
2606
0
          case TIMETZOID: /* Nothing to do for TIMETZ */
2607
0
            break;
2608
0
          case TIMESTAMPTZOID:
2609
0
            value = DirectFunctionCall1(timestamptz_timetz,
2610
0
                          value);
2611
0
            break;
2612
0
          default:
2613
0
            elog(ERROR, "type with oid %u not supported", typid);
2614
0
        }
2615
2616
        /* Force the user-given time precision, if any */
2617
0
        if (time_precision != -1)
2618
0
        {
2619
0
          TimeTzADT  *result;
2620
2621
          /* Get a warning when precision is reduced */
2622
0
          time_precision = anytime_typmod_check(true,
2623
0
                              time_precision);
2624
0
          result = DatumGetTimeTzADTP(value);
2625
0
          AdjustTimeForTypmod(&result->time, time_precision);
2626
0
          value = TimeTzADTPGetDatum(result);
2627
2628
          /* Update the typmod value with the user-given precision */
2629
0
          typmod = time_precision;
2630
0
        }
2631
2632
0
        typid = TIMETZOID;
2633
0
      }
2634
0
      break;
2635
0
    case jpiTimestamp:
2636
0
      {
2637
        /* Convert result type to timestamp without time zone */
2638
0
        switch (typid)
2639
0
        {
2640
0
          case DATEOID:
2641
0
            value = DirectFunctionCall1(date_timestamp,
2642
0
                          value);
2643
0
            break;
2644
0
          case TIMEOID:
2645
0
          case TIMETZOID:
2646
0
            RETURN_ERROR(ereport(ERROR,
2647
0
                       (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
2648
0
                        errmsg("%s format is not recognized: \"%s\"",
2649
0
                           "timestamp", text_to_cstring(datetime)))));
2650
0
            break;
2651
0
          case TIMESTAMPOID: /* Nothing to do for TIMESTAMP */
2652
0
            break;
2653
0
          case TIMESTAMPTZOID:
2654
0
            checkTimezoneIsUsedForCast(cxt->useTz,
2655
0
                           "timestamptz", "timestamp");
2656
0
            value = DirectFunctionCall1(timestamptz_timestamp,
2657
0
                          value);
2658
0
            break;
2659
0
          default:
2660
0
            elog(ERROR, "type with oid %u not supported", typid);
2661
0
        }
2662
2663
        /* Force the user-given time precision, if any */
2664
0
        if (time_precision != -1)
2665
0
        {
2666
0
          Timestamp result;
2667
0
          ErrorSaveContext escontext = {T_ErrorSaveContext};
2668
2669
          /* Get a warning when precision is reduced */
2670
0
          time_precision = anytimestamp_typmod_check(false,
2671
0
                                 time_precision);
2672
0
          result = DatumGetTimestamp(value);
2673
0
          AdjustTimestampForTypmod(&result, time_precision,
2674
0
                       (Node *) &escontext);
2675
0
          if (escontext.error_occurred) /* should not happen */
2676
0
            RETURN_ERROR(ereport(ERROR,
2677
0
                       (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
2678
0
                        errmsg("time precision of jsonpath item method .%s() is invalid",
2679
0
                           jspOperationName(jsp->type)))));
2680
0
          value = TimestampGetDatum(result);
2681
2682
          /* Update the typmod value with the user-given precision */
2683
0
          typmod = time_precision;
2684
0
        }
2685
2686
0
        typid = TIMESTAMPOID;
2687
0
      }
2688
0
      break;
2689
0
    case jpiTimestampTz:
2690
0
      {
2691
0
        struct pg_tm tm;
2692
0
        fsec_t    fsec;
2693
2694
        /* Convert result type to timestamp with time zone */
2695
0
        switch (typid)
2696
0
        {
2697
0
          case DATEOID:
2698
0
            checkTimezoneIsUsedForCast(cxt->useTz,
2699
0
                           "date", "timestamptz");
2700
2701
            /*
2702
             * Get the timezone value explicitly since JsonbValue
2703
             * keeps that separate.
2704
             */
2705
0
            j2date(DatumGetDateADT(value) + POSTGRES_EPOCH_JDATE,
2706
0
                 &(tm.tm_year), &(tm.tm_mon), &(tm.tm_mday));
2707
0
            tm.tm_hour = 0;
2708
0
            tm.tm_min = 0;
2709
0
            tm.tm_sec = 0;
2710
0
            tz = DetermineTimeZoneOffset(&tm, session_timezone);
2711
2712
0
            value = DirectFunctionCall1(date_timestamptz,
2713
0
                          value);
2714
0
            break;
2715
0
          case TIMEOID:
2716
0
          case TIMETZOID:
2717
0
            RETURN_ERROR(ereport(ERROR,
2718
0
                       (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
2719
0
                        errmsg("%s format is not recognized: \"%s\"",
2720
0
                           "timestamp_tz", text_to_cstring(datetime)))));
2721
0
            break;
2722
0
          case TIMESTAMPOID:
2723
0
            checkTimezoneIsUsedForCast(cxt->useTz,
2724
0
                           "timestamp", "timestamptz");
2725
2726
            /*
2727
             * Get the timezone value explicitly since JsonbValue
2728
             * keeps that separate.
2729
             */
2730
0
            if (timestamp2tm(DatumGetTimestamp(value), NULL, &tm,
2731
0
                     &fsec, NULL, NULL) == 0)
2732
0
              tz = DetermineTimeZoneOffset(&tm,
2733
0
                             session_timezone);
2734
2735
0
            value = DirectFunctionCall1(timestamp_timestamptz,
2736
0
                          value);
2737
0
            break;
2738
0
          case TIMESTAMPTZOID: /* Nothing to do for TIMESTAMPTZ */
2739
0
            break;
2740
0
          default:
2741
0
            elog(ERROR, "type with oid %u not supported", typid);
2742
0
        }
2743
2744
        /* Force the user-given time precision, if any */
2745
0
        if (time_precision != -1)
2746
0
        {
2747
0
          Timestamp result;
2748
0
          ErrorSaveContext escontext = {T_ErrorSaveContext};
2749
2750
          /* Get a warning when precision is reduced */
2751
0
          time_precision = anytimestamp_typmod_check(true,
2752
0
                                 time_precision);
2753
0
          result = DatumGetTimestampTz(value);
2754
0
          AdjustTimestampForTypmod(&result, time_precision,
2755
0
                       (Node *) &escontext);
2756
0
          if (escontext.error_occurred) /* should not happen */
2757
0
            RETURN_ERROR(ereport(ERROR,
2758
0
                       (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
2759
0
                        errmsg("time precision of jsonpath item method .%s() is invalid",
2760
0
                           jspOperationName(jsp->type)))));
2761
0
          value = TimestampTzGetDatum(result);
2762
2763
          /* Update the typmod value with the user-given precision */
2764
0
          typmod = time_precision;
2765
0
        }
2766
2767
0
        typid = TIMESTAMPTZOID;
2768
0
      }
2769
0
      break;
2770
0
    default:
2771
0
      elog(ERROR, "unrecognized jsonpath item type: %d", jsp->type);
2772
0
  }
2773
2774
0
  pfree(datetime);
2775
2776
0
  if (jperIsError(res))
2777
0
    return res;
2778
2779
0
  hasNext = jspGetNext(jsp, &elem);
2780
2781
0
  if (!hasNext && !found)
2782
0
    return res;
2783
2784
0
  jb = hasNext ? &jbvbuf : palloc(sizeof(*jb));
2785
2786
0
  jb->type = jbvDatetime;
2787
0
  jb->val.datetime.value = value;
2788
0
  jb->val.datetime.typid = typid;
2789
0
  jb->val.datetime.typmod = typmod;
2790
0
  jb->val.datetime.tz = tz;
2791
2792
0
  return executeNextItem(cxt, jsp, &elem, jb, found, hasNext);
2793
0
}
2794
2795
/*
2796
 * Implementation of .keyvalue() method.
2797
 *
2798
 * .keyvalue() method returns a sequence of object's key-value pairs in the
2799
 * following format: '{ "key": key, "value": value, "id": id }'.
2800
 *
2801
 * "id" field is an object identifier which is constructed from the two parts:
2802
 * base object id and its binary offset in base object's jsonb:
2803
 * id = 10000000000 * base_object_id + obj_offset_in_base_object
2804
 *
2805
 * 10000000000 (10^10) -- is a first round decimal number greater than 2^32
2806
 * (maximal offset in jsonb).  Decimal multiplier is used here to improve the
2807
 * readability of identifiers.
2808
 *
2809
 * Base object is usually a root object of the path: context item '$' or path
2810
 * variable '$var', literals can't produce objects for now.  But if the path
2811
 * contains generated objects (.keyvalue() itself, for example), then they
2812
 * become base object for the subsequent .keyvalue().
2813
 *
2814
 * Id of '$' is 0. Id of '$var' is its ordinal (positive) number in the list
2815
 * of variables (see getJsonPathVariable()).  Ids for generated objects
2816
 * are assigned using global counter JsonPathExecContext.lastGeneratedObjectId.
2817
 */
2818
static JsonPathExecResult
2819
executeKeyValueMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
2820
            JsonbValue *jb, JsonValueList *found)
2821
0
{
2822
0
  JsonPathExecResult res = jperNotFound;
2823
0
  JsonPathItem next;
2824
0
  JsonbContainer *jbc;
2825
0
  JsonbValue  key;
2826
0
  JsonbValue  val;
2827
0
  JsonbValue  idval;
2828
0
  JsonbValue  keystr;
2829
0
  JsonbValue  valstr;
2830
0
  JsonbValue  idstr;
2831
0
  JsonbIterator *it;
2832
0
  JsonbIteratorToken tok;
2833
0
  int64   id;
2834
0
  bool    hasNext;
2835
2836
0
  if (JsonbType(jb) != jbvObject || jb->type != jbvBinary)
2837
0
    RETURN_ERROR(ereport(ERROR,
2838
0
               (errcode(ERRCODE_SQL_JSON_OBJECT_NOT_FOUND),
2839
0
                errmsg("jsonpath item method .%s() can only be applied to an object",
2840
0
                   jspOperationName(jsp->type)))));
2841
2842
0
  jbc = jb->val.binary.data;
2843
2844
0
  if (!JsonContainerSize(jbc))
2845
0
    return jperNotFound; /* no key-value pairs */
2846
2847
0
  hasNext = jspGetNext(jsp, &next);
2848
2849
0
  keystr.type = jbvString;
2850
0
  keystr.val.string.val = "key";
2851
0
  keystr.val.string.len = 3;
2852
2853
0
  valstr.type = jbvString;
2854
0
  valstr.val.string.val = "value";
2855
0
  valstr.val.string.len = 5;
2856
2857
0
  idstr.type = jbvString;
2858
0
  idstr.val.string.val = "id";
2859
0
  idstr.val.string.len = 2;
2860
2861
  /* construct object id from its base object and offset inside that */
2862
0
  id = jb->type != jbvBinary ? 0 :
2863
0
    (int64) ((char *) jbc - (char *) cxt->baseObject.jbc);
2864
0
  id += (int64) cxt->baseObject.id * INT64CONST(10000000000);
2865
2866
0
  idval.type = jbvNumeric;
2867
0
  idval.val.numeric = int64_to_numeric(id);
2868
2869
0
  it = JsonbIteratorInit(jbc);
2870
2871
0
  while ((tok = JsonbIteratorNext(&it, &key, true)) != WJB_DONE)
2872
0
  {
2873
0
    JsonBaseObjectInfo baseObject;
2874
0
    JsonbValue  obj;
2875
0
    JsonbParseState *ps;
2876
0
    JsonbValue *keyval;
2877
0
    Jsonb    *jsonb;
2878
2879
0
    if (tok != WJB_KEY)
2880
0
      continue;
2881
2882
0
    res = jperOk;
2883
2884
0
    if (!hasNext && !found)
2885
0
      break;
2886
2887
0
    tok = JsonbIteratorNext(&it, &val, true);
2888
0
    Assert(tok == WJB_VALUE);
2889
2890
0
    ps = NULL;
2891
0
    pushJsonbValue(&ps, WJB_BEGIN_OBJECT, NULL);
2892
2893
0
    pushJsonbValue(&ps, WJB_KEY, &keystr);
2894
0
    pushJsonbValue(&ps, WJB_VALUE, &key);
2895
2896
0
    pushJsonbValue(&ps, WJB_KEY, &valstr);
2897
0
    pushJsonbValue(&ps, WJB_VALUE, &val);
2898
2899
0
    pushJsonbValue(&ps, WJB_KEY, &idstr);
2900
0
    pushJsonbValue(&ps, WJB_VALUE, &idval);
2901
2902
0
    keyval = pushJsonbValue(&ps, WJB_END_OBJECT, NULL);
2903
2904
0
    jsonb = JsonbValueToJsonb(keyval);
2905
2906
0
    JsonbInitBinary(&obj, jsonb);
2907
2908
0
    baseObject = setBaseObject(cxt, &obj, cxt->lastGeneratedObjectId++);
2909
2910
0
    res = executeNextItem(cxt, jsp, &next, &obj, found, true);
2911
2912
0
    cxt->baseObject = baseObject;
2913
2914
0
    if (jperIsError(res))
2915
0
      return res;
2916
2917
0
    if (res == jperOk && !found)
2918
0
      break;
2919
0
  }
2920
2921
0
  return res;
2922
0
}
2923
2924
/*
2925
 * Convert boolean execution status 'res' to a boolean JSON item and execute
2926
 * next jsonpath.
2927
 */
2928
static JsonPathExecResult
2929
appendBoolResult(JsonPathExecContext *cxt, JsonPathItem *jsp,
2930
         JsonValueList *found, JsonPathBool res)
2931
0
{
2932
0
  JsonPathItem next;
2933
0
  JsonbValue  jbv;
2934
2935
0
  if (!jspGetNext(jsp, &next) && !found)
2936
0
    return jperOk;     /* found singleton boolean value */
2937
2938
0
  if (res == jpbUnknown)
2939
0
  {
2940
0
    jbv.type = jbvNull;
2941
0
  }
2942
0
  else
2943
0
  {
2944
0
    jbv.type = jbvBool;
2945
0
    jbv.val.boolean = res == jpbTrue;
2946
0
  }
2947
2948
0
  return executeNextItem(cxt, jsp, &next, &jbv, found, true);
2949
0
}
2950
2951
/*
2952
 * Convert jsonpath's scalar or variable node to actual jsonb value.
2953
 *
2954
 * If node is a variable then its id returned, otherwise 0 returned.
2955
 */
2956
static void
2957
getJsonPathItem(JsonPathExecContext *cxt, JsonPathItem *item,
2958
        JsonbValue *value)
2959
0
{
2960
0
  switch (item->type)
2961
0
  {
2962
0
    case jpiNull:
2963
0
      value->type = jbvNull;
2964
0
      break;
2965
0
    case jpiBool:
2966
0
      value->type = jbvBool;
2967
0
      value->val.boolean = jspGetBool(item);
2968
0
      break;
2969
0
    case jpiNumeric:
2970
0
      value->type = jbvNumeric;
2971
0
      value->val.numeric = jspGetNumeric(item);
2972
0
      break;
2973
0
    case jpiString:
2974
0
      value->type = jbvString;
2975
0
      value->val.string.val = jspGetString(item,
2976
0
                         &value->val.string.len);
2977
0
      break;
2978
0
    case jpiVariable:
2979
0
      getJsonPathVariable(cxt, item, value);
2980
0
      return;
2981
0
    default:
2982
0
      elog(ERROR, "unexpected jsonpath item type");
2983
0
  }
2984
0
}
2985
2986
/*
2987
 * Returns the computed value of a JSON path variable with given name.
2988
 */
2989
static JsonbValue *
2990
GetJsonPathVar(void *cxt, char *varName, int varNameLen,
2991
         JsonbValue *baseObject, int *baseObjectId)
2992
0
{
2993
0
  JsonPathVariable *var = NULL;
2994
0
  List     *vars = cxt;
2995
0
  ListCell   *lc;
2996
0
  JsonbValue *result;
2997
0
  int     id = 1;
2998
2999
0
  foreach(lc, vars)
3000
0
  {
3001
0
    JsonPathVariable *curvar = lfirst(lc);
3002
3003
0
    if (curvar->namelen == varNameLen &&
3004
0
      strncmp(curvar->name, varName, varNameLen) == 0)
3005
0
    {
3006
0
      var = curvar;
3007
0
      break;
3008
0
    }
3009
3010
0
    id++;
3011
0
  }
3012
3013
0
  if (var == NULL)
3014
0
  {
3015
0
    *baseObjectId = -1;
3016
0
    return NULL;
3017
0
  }
3018
3019
0
  result = palloc(sizeof(JsonbValue));
3020
0
  if (var->isnull)
3021
0
  {
3022
0
    *baseObjectId = 0;
3023
0
    result->type = jbvNull;
3024
0
  }
3025
0
  else
3026
0
    JsonItemFromDatum(var->value, var->typid, var->typmod, result);
3027
3028
0
  *baseObject = *result;
3029
0
  *baseObjectId = id;
3030
3031
0
  return result;
3032
0
}
3033
3034
static int
3035
CountJsonPathVars(void *cxt)
3036
0
{
3037
0
  List     *vars = (List *) cxt;
3038
3039
0
  return list_length(vars);
3040
0
}
3041
3042
3043
/*
3044
 * Initialize JsonbValue to pass to jsonpath executor from given
3045
 * datum value of the specified type.
3046
 */
3047
static void
3048
JsonItemFromDatum(Datum val, Oid typid, int32 typmod, JsonbValue *res)
3049
0
{
3050
0
  switch (typid)
3051
0
  {
3052
0
    case BOOLOID:
3053
0
      res->type = jbvBool;
3054
0
      res->val.boolean = DatumGetBool(val);
3055
0
      break;
3056
0
    case NUMERICOID:
3057
0
      JsonbValueInitNumericDatum(res, val);
3058
0
      break;
3059
0
    case INT2OID:
3060
0
      JsonbValueInitNumericDatum(res, DirectFunctionCall1(int2_numeric, val));
3061
0
      break;
3062
0
    case INT4OID:
3063
0
      JsonbValueInitNumericDatum(res, DirectFunctionCall1(int4_numeric, val));
3064
0
      break;
3065
0
    case INT8OID:
3066
0
      JsonbValueInitNumericDatum(res, DirectFunctionCall1(int8_numeric, val));
3067
0
      break;
3068
0
    case FLOAT4OID:
3069
0
      JsonbValueInitNumericDatum(res, DirectFunctionCall1(float4_numeric, val));
3070
0
      break;
3071
0
    case FLOAT8OID:
3072
0
      JsonbValueInitNumericDatum(res, DirectFunctionCall1(float8_numeric, val));
3073
0
      break;
3074
0
    case TEXTOID:
3075
0
    case VARCHAROID:
3076
0
      res->type = jbvString;
3077
0
      res->val.string.val = VARDATA_ANY(val);
3078
0
      res->val.string.len = VARSIZE_ANY_EXHDR(val);
3079
0
      break;
3080
0
    case DATEOID:
3081
0
    case TIMEOID:
3082
0
    case TIMETZOID:
3083
0
    case TIMESTAMPOID:
3084
0
    case TIMESTAMPTZOID:
3085
0
      res->type = jbvDatetime;
3086
0
      res->val.datetime.value = val;
3087
0
      res->val.datetime.typid = typid;
3088
0
      res->val.datetime.typmod = typmod;
3089
0
      res->val.datetime.tz = 0;
3090
0
      break;
3091
0
    case JSONBOID:
3092
0
      {
3093
0
        JsonbValue *jbv = res;
3094
0
        Jsonb    *jb = DatumGetJsonbP(val);
3095
3096
0
        if (JsonContainerIsScalar(&jb->root))
3097
0
        {
3098
0
          bool    result PG_USED_FOR_ASSERTS_ONLY;
3099
3100
0
          result = JsonbExtractScalar(&jb->root, jbv);
3101
0
          Assert(result);
3102
0
        }
3103
0
        else
3104
0
          JsonbInitBinary(jbv, jb);
3105
0
        break;
3106
0
      }
3107
0
    case JSONOID:
3108
0
      {
3109
0
        text     *txt = DatumGetTextP(val);
3110
0
        char     *str = text_to_cstring(txt);
3111
0
        Jsonb    *jb;
3112
3113
0
        jb = DatumGetJsonbP(DirectFunctionCall1(jsonb_in,
3114
0
                            CStringGetDatum(str)));
3115
0
        pfree(str);
3116
3117
0
        JsonItemFromDatum(JsonbPGetDatum(jb), JSONBOID, -1, res);
3118
0
        break;
3119
0
      }
3120
0
    default:
3121
0
      ereport(ERROR,
3122
0
          errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3123
0
          errmsg("could not convert value of type %s to jsonpath",
3124
0
               format_type_be(typid)));
3125
0
  }
3126
0
}
3127
3128
/* Initialize numeric value from the given datum */
3129
static void
3130
JsonbValueInitNumericDatum(JsonbValue *jbv, Datum num)
3131
0
{
3132
0
  jbv->type = jbvNumeric;
3133
0
  jbv->val.numeric = DatumGetNumeric(num);
3134
0
}
3135
3136
/*
3137
 * Get the value of variable passed to jsonpath executor
3138
 */
3139
static void
3140
getJsonPathVariable(JsonPathExecContext *cxt, JsonPathItem *variable,
3141
          JsonbValue *value)
3142
0
{
3143
0
  char     *varName;
3144
0
  int     varNameLength;
3145
0
  JsonbValue  baseObject;
3146
0
  int     baseObjectId;
3147
0
  JsonbValue *v;
3148
3149
0
  Assert(variable->type == jpiVariable);
3150
0
  varName = jspGetString(variable, &varNameLength);
3151
3152
0
  if (cxt->vars == NULL ||
3153
0
    (v = cxt->getVar(cxt->vars, varName, varNameLength,
3154
0
             &baseObject, &baseObjectId)) == NULL)
3155
0
    ereport(ERROR,
3156
0
        (errcode(ERRCODE_UNDEFINED_OBJECT),
3157
0
         errmsg("could not find jsonpath variable \"%s\"",
3158
0
            pnstrdup(varName, varNameLength))));
3159
3160
0
  if (baseObjectId > 0)
3161
0
  {
3162
0
    *value = *v;
3163
0
    setBaseObject(cxt, &baseObject, baseObjectId);
3164
0
  }
3165
0
}
3166
3167
/*
3168
 * Definition of JsonPathGetVarCallback for when JsonPathExecContext.vars
3169
 * is specified as a jsonb value.
3170
 */
3171
static JsonbValue *
3172
getJsonPathVariableFromJsonb(void *varsJsonb, char *varName, int varNameLength,
3173
               JsonbValue *baseObject, int *baseObjectId)
3174
0
{
3175
0
  Jsonb    *vars = varsJsonb;
3176
0
  JsonbValue  tmp;
3177
0
  JsonbValue *result;
3178
3179
0
  tmp.type = jbvString;
3180
0
  tmp.val.string.val = varName;
3181
0
  tmp.val.string.len = varNameLength;
3182
3183
0
  result = findJsonbValueFromContainer(&vars->root, JB_FOBJECT, &tmp);
3184
3185
0
  if (result == NULL)
3186
0
  {
3187
0
    *baseObjectId = -1;
3188
0
    return NULL;
3189
0
  }
3190
3191
0
  *baseObjectId = 1;
3192
0
  JsonbInitBinary(baseObject, vars);
3193
3194
0
  return result;
3195
0
}
3196
3197
/*
3198
 * Definition of JsonPathCountVarsCallback for when JsonPathExecContext.vars
3199
 * is specified as a jsonb value.
3200
 */
3201
static int
3202
countVariablesFromJsonb(void *varsJsonb)
3203
0
{
3204
0
  Jsonb    *vars = varsJsonb;
3205
3206
0
  if (vars && !JsonContainerIsObject(&vars->root))
3207
0
  {
3208
0
    ereport(ERROR,
3209
0
        errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3210
0
        errmsg("\"vars\" argument is not an object"),
3211
0
        errdetail("Jsonpath parameters should be encoded as key-value pairs of \"vars\" object."));
3212
0
  }
3213
3214
  /* count of base objects */
3215
0
  return vars != NULL ? 1 : 0;
3216
0
}
3217
3218
/**************** Support functions for JsonPath execution *****************/
3219
3220
/*
3221
 * Returns the size of an array item, or -1 if item is not an array.
3222
 */
3223
static int
3224
JsonbArraySize(JsonbValue *jb)
3225
0
{
3226
0
  Assert(jb->type != jbvArray);
3227
3228
0
  if (jb->type == jbvBinary)
3229
0
  {
3230
0
    JsonbContainer *jbc = jb->val.binary.data;
3231
3232
0
    if (JsonContainerIsArray(jbc) && !JsonContainerIsScalar(jbc))
3233
0
      return JsonContainerSize(jbc);
3234
0
  }
3235
3236
0
  return -1;
3237
0
}
3238
3239
/* Comparison predicate callback. */
3240
static JsonPathBool
3241
executeComparison(JsonPathItem *cmp, JsonbValue *lv, JsonbValue *rv, void *p)
3242
0
{
3243
0
  JsonPathExecContext *cxt = (JsonPathExecContext *) p;
3244
3245
0
  return compareItems(cmp->type, lv, rv, cxt->useTz);
3246
0
}
3247
3248
/*
3249
 * Perform per-byte comparison of two strings.
3250
 */
3251
static int
3252
binaryCompareStrings(const char *s1, int len1,
3253
           const char *s2, int len2)
3254
0
{
3255
0
  int     cmp;
3256
3257
0
  cmp = memcmp(s1, s2, Min(len1, len2));
3258
3259
0
  if (cmp != 0)
3260
0
    return cmp;
3261
3262
0
  if (len1 == len2)
3263
0
    return 0;
3264
3265
0
  return len1 < len2 ? -1 : 1;
3266
0
}
3267
3268
/*
3269
 * Compare two strings in the current server encoding using Unicode codepoint
3270
 * collation.
3271
 */
3272
static int
3273
compareStrings(const char *mbstr1, int mblen1,
3274
         const char *mbstr2, int mblen2)
3275
0
{
3276
0
  if (GetDatabaseEncoding() == PG_SQL_ASCII ||
3277
0
    GetDatabaseEncoding() == PG_UTF8)
3278
0
  {
3279
    /*
3280
     * It's known property of UTF-8 strings that their per-byte comparison
3281
     * result matches codepoints comparison result.  ASCII can be
3282
     * considered as special case of UTF-8.
3283
     */
3284
0
    return binaryCompareStrings(mbstr1, mblen1, mbstr2, mblen2);
3285
0
  }
3286
0
  else
3287
0
  {
3288
0
    char     *utf8str1,
3289
0
           *utf8str2;
3290
0
    int     cmp,
3291
0
          utf8len1,
3292
0
          utf8len2;
3293
3294
    /*
3295
     * We have to convert other encodings to UTF-8 first, then compare.
3296
     * Input strings may be not null-terminated and pg_server_to_any() may
3297
     * return them "as is".  So, use strlen() only if there is real
3298
     * conversion.
3299
     */
3300
0
    utf8str1 = pg_server_to_any(mbstr1, mblen1, PG_UTF8);
3301
0
    utf8str2 = pg_server_to_any(mbstr2, mblen2, PG_UTF8);
3302
0
    utf8len1 = (mbstr1 == utf8str1) ? mblen1 : strlen(utf8str1);
3303
0
    utf8len2 = (mbstr2 == utf8str2) ? mblen2 : strlen(utf8str2);
3304
3305
0
    cmp = binaryCompareStrings(utf8str1, utf8len1, utf8str2, utf8len2);
3306
3307
    /*
3308
     * If pg_server_to_any() did no real conversion, then we actually
3309
     * compared original strings.  So, we already done.
3310
     */
3311
0
    if (mbstr1 == utf8str1 && mbstr2 == utf8str2)
3312
0
      return cmp;
3313
3314
    /* Free memory if needed */
3315
0
    if (mbstr1 != utf8str1)
3316
0
      pfree(utf8str1);
3317
0
    if (mbstr2 != utf8str2)
3318
0
      pfree(utf8str2);
3319
3320
    /*
3321
     * When all Unicode codepoints are equal, return result of binary
3322
     * comparison.  In some edge cases, same characters may have different
3323
     * representations in encoding.  Then our behavior could diverge from
3324
     * standard.  However, that allow us to do simple binary comparison
3325
     * for "==" operator, which is performance critical in typical cases.
3326
     * In future to implement strict standard conformance, we can do
3327
     * normalization of input JSON strings.
3328
     */
3329
0
    if (cmp == 0)
3330
0
      return binaryCompareStrings(mbstr1, mblen1, mbstr2, mblen2);
3331
0
    else
3332
0
      return cmp;
3333
0
  }
3334
0
}
3335
3336
/*
3337
 * Compare two SQL/JSON items using comparison operation 'op'.
3338
 */
3339
static JsonPathBool
3340
compareItems(int32 op, JsonbValue *jb1, JsonbValue *jb2, bool useTz)
3341
0
{
3342
0
  int     cmp;
3343
0
  bool    res;
3344
3345
0
  if (jb1->type != jb2->type)
3346
0
  {
3347
0
    if (jb1->type == jbvNull || jb2->type == jbvNull)
3348
3349
      /*
3350
       * Equality and order comparison of nulls to non-nulls returns
3351
       * always false, but inequality comparison returns true.
3352
       */
3353
0
      return op == jpiNotEqual ? jpbTrue : jpbFalse;
3354
3355
    /* Non-null items of different types are not comparable. */
3356
0
    return jpbUnknown;
3357
0
  }
3358
3359
0
  switch (jb1->type)
3360
0
  {
3361
0
    case jbvNull:
3362
0
      cmp = 0;
3363
0
      break;
3364
0
    case jbvBool:
3365
0
      cmp = jb1->val.boolean == jb2->val.boolean ? 0 :
3366
0
        jb1->val.boolean ? 1 : -1;
3367
0
      break;
3368
0
    case jbvNumeric:
3369
0
      cmp = compareNumeric(jb1->val.numeric, jb2->val.numeric);
3370
0
      break;
3371
0
    case jbvString:
3372
0
      if (op == jpiEqual)
3373
0
        return jb1->val.string.len != jb2->val.string.len ||
3374
0
          memcmp(jb1->val.string.val,
3375
0
               jb2->val.string.val,
3376
0
               jb1->val.string.len) ? jpbFalse : jpbTrue;
3377
3378
0
      cmp = compareStrings(jb1->val.string.val, jb1->val.string.len,
3379
0
                 jb2->val.string.val, jb2->val.string.len);
3380
0
      break;
3381
0
    case jbvDatetime:
3382
0
      {
3383
0
        bool    cast_error;
3384
3385
0
        cmp = compareDatetime(jb1->val.datetime.value,
3386
0
                    jb1->val.datetime.typid,
3387
0
                    jb2->val.datetime.value,
3388
0
                    jb2->val.datetime.typid,
3389
0
                    useTz,
3390
0
                    &cast_error);
3391
3392
0
        if (cast_error)
3393
0
          return jpbUnknown;
3394
0
      }
3395
0
      break;
3396
3397
0
    case jbvBinary:
3398
0
    case jbvArray:
3399
0
    case jbvObject:
3400
0
      return jpbUnknown; /* non-scalars are not comparable */
3401
3402
0
    default:
3403
0
      elog(ERROR, "invalid jsonb value type %d", jb1->type);
3404
0
  }
3405
3406
0
  switch (op)
3407
0
  {
3408
0
    case jpiEqual:
3409
0
      res = (cmp == 0);
3410
0
      break;
3411
0
    case jpiNotEqual:
3412
0
      res = (cmp != 0);
3413
0
      break;
3414
0
    case jpiLess:
3415
0
      res = (cmp < 0);
3416
0
      break;
3417
0
    case jpiGreater:
3418
0
      res = (cmp > 0);
3419
0
      break;
3420
0
    case jpiLessOrEqual:
3421
0
      res = (cmp <= 0);
3422
0
      break;
3423
0
    case jpiGreaterOrEqual:
3424
0
      res = (cmp >= 0);
3425
0
      break;
3426
0
    default:
3427
0
      elog(ERROR, "unrecognized jsonpath operation: %d", op);
3428
0
      return jpbUnknown;
3429
0
  }
3430
3431
0
  return res ? jpbTrue : jpbFalse;
3432
0
}
3433
3434
/* Compare two numerics */
3435
static int
3436
compareNumeric(Numeric a, Numeric b)
3437
0
{
3438
0
  return DatumGetInt32(DirectFunctionCall2(numeric_cmp,
3439
0
                       NumericGetDatum(a),
3440
0
                       NumericGetDatum(b)));
3441
0
}
3442
3443
static JsonbValue *
3444
copyJsonbValue(JsonbValue *src)
3445
0
{
3446
0
  JsonbValue *dst = palloc(sizeof(*dst));
3447
3448
0
  *dst = *src;
3449
3450
0
  return dst;
3451
0
}
3452
3453
/*
3454
 * Execute array subscript expression and convert resulting numeric item to
3455
 * the integer type with truncation.
3456
 */
3457
static JsonPathExecResult
3458
getArrayIndex(JsonPathExecContext *cxt, JsonPathItem *jsp, JsonbValue *jb,
3459
        int32 *index)
3460
0
{
3461
0
  JsonbValue *jbv;
3462
0
  JsonValueList found = {0};
3463
0
  JsonPathExecResult res = executeItem(cxt, jsp, jb, &found);
3464
0
  Datum   numeric_index;
3465
0
  bool    have_error = false;
3466
3467
0
  if (jperIsError(res))
3468
0
    return res;
3469
3470
0
  if (JsonValueListLength(&found) != 1 ||
3471
0
    !(jbv = getScalar(JsonValueListHead(&found), jbvNumeric)))
3472
0
    RETURN_ERROR(ereport(ERROR,
3473
0
               (errcode(ERRCODE_INVALID_SQL_JSON_SUBSCRIPT),
3474
0
                errmsg("jsonpath array subscript is not a single numeric value"))));
3475
3476
0
  numeric_index = DirectFunctionCall2(numeric_trunc,
3477
0
                    NumericGetDatum(jbv->val.numeric),
3478
0
                    Int32GetDatum(0));
3479
3480
0
  *index = numeric_int4_opt_error(DatumGetNumeric(numeric_index),
3481
0
                  &have_error);
3482
3483
0
  if (have_error)
3484
0
    RETURN_ERROR(ereport(ERROR,
3485
0
               (errcode(ERRCODE_INVALID_SQL_JSON_SUBSCRIPT),
3486
0
                errmsg("jsonpath array subscript is out of integer range"))));
3487
3488
0
  return jperOk;
3489
0
}
3490
3491
/* Save base object and its id needed for the execution of .keyvalue(). */
3492
static JsonBaseObjectInfo
3493
setBaseObject(JsonPathExecContext *cxt, JsonbValue *jbv, int32 id)
3494
0
{
3495
0
  JsonBaseObjectInfo baseObject = cxt->baseObject;
3496
3497
0
  cxt->baseObject.jbc = jbv->type != jbvBinary ? NULL :
3498
0
    (JsonbContainer *) jbv->val.binary.data;
3499
0
  cxt->baseObject.id = id;
3500
3501
0
  return baseObject;
3502
0
}
3503
3504
static void
3505
JsonValueListClear(JsonValueList *jvl)
3506
0
{
3507
0
  jvl->singleton = NULL;
3508
0
  jvl->list = NIL;
3509
0
}
3510
3511
static void
3512
JsonValueListAppend(JsonValueList *jvl, JsonbValue *jbv)
3513
0
{
3514
0
  if (jvl->singleton)
3515
0
  {
3516
0
    jvl->list = list_make2(jvl->singleton, jbv);
3517
0
    jvl->singleton = NULL;
3518
0
  }
3519
0
  else if (!jvl->list)
3520
0
    jvl->singleton = jbv;
3521
0
  else
3522
0
    jvl->list = lappend(jvl->list, jbv);
3523
0
}
3524
3525
static int
3526
JsonValueListLength(const JsonValueList *jvl)
3527
0
{
3528
0
  return jvl->singleton ? 1 : list_length(jvl->list);
3529
0
}
3530
3531
static bool
3532
JsonValueListIsEmpty(JsonValueList *jvl)
3533
0
{
3534
0
  return !jvl->singleton && (jvl->list == NIL);
3535
0
}
3536
3537
static JsonbValue *
3538
JsonValueListHead(JsonValueList *jvl)
3539
0
{
3540
0
  return jvl->singleton ? jvl->singleton : linitial(jvl->list);
3541
0
}
3542
3543
static List *
3544
JsonValueListGetList(JsonValueList *jvl)
3545
0
{
3546
0
  if (jvl->singleton)
3547
0
    return list_make1(jvl->singleton);
3548
3549
0
  return jvl->list;
3550
0
}
3551
3552
static void
3553
JsonValueListInitIterator(const JsonValueList *jvl, JsonValueListIterator *it)
3554
0
{
3555
0
  if (jvl->singleton)
3556
0
  {
3557
0
    it->value = jvl->singleton;
3558
0
    it->list = NIL;
3559
0
    it->next = NULL;
3560
0
  }
3561
0
  else if (jvl->list != NIL)
3562
0
  {
3563
0
    it->value = (JsonbValue *) linitial(jvl->list);
3564
0
    it->list = jvl->list;
3565
0
    it->next = list_second_cell(jvl->list);
3566
0
  }
3567
0
  else
3568
0
  {
3569
0
    it->value = NULL;
3570
0
    it->list = NIL;
3571
0
    it->next = NULL;
3572
0
  }
3573
0
}
3574
3575
/*
3576
 * Get the next item from the sequence advancing iterator.
3577
 */
3578
static JsonbValue *
3579
JsonValueListNext(const JsonValueList *jvl, JsonValueListIterator *it)
3580
0
{
3581
0
  JsonbValue *result = it->value;
3582
3583
0
  if (it->next)
3584
0
  {
3585
0
    it->value = lfirst(it->next);
3586
0
    it->next = lnext(it->list, it->next);
3587
0
  }
3588
0
  else
3589
0
  {
3590
0
    it->value = NULL;
3591
0
  }
3592
3593
0
  return result;
3594
0
}
3595
3596
/*
3597
 * Initialize a binary JsonbValue with the given jsonb container.
3598
 */
3599
static JsonbValue *
3600
JsonbInitBinary(JsonbValue *jbv, Jsonb *jb)
3601
0
{
3602
0
  jbv->type = jbvBinary;
3603
0
  jbv->val.binary.data = &jb->root;
3604
0
  jbv->val.binary.len = VARSIZE_ANY_EXHDR(jb);
3605
3606
0
  return jbv;
3607
0
}
3608
3609
/*
3610
 * Returns jbv* type of JsonbValue. Note, it never returns jbvBinary as is.
3611
 */
3612
static int
3613
JsonbType(JsonbValue *jb)
3614
0
{
3615
0
  int     type = jb->type;
3616
3617
0
  if (jb->type == jbvBinary)
3618
0
  {
3619
0
    JsonbContainer *jbc = jb->val.binary.data;
3620
3621
    /* Scalars should be always extracted during jsonpath execution. */
3622
0
    Assert(!JsonContainerIsScalar(jbc));
3623
3624
0
    if (JsonContainerIsObject(jbc))
3625
0
      type = jbvObject;
3626
0
    else if (JsonContainerIsArray(jbc))
3627
0
      type = jbvArray;
3628
0
    else
3629
0
      elog(ERROR, "invalid jsonb container type: 0x%08x", jbc->header);
3630
0
  }
3631
3632
0
  return type;
3633
0
}
3634
3635
/* Get scalar of given type or NULL on type mismatch */
3636
static JsonbValue *
3637
getScalar(JsonbValue *scalar, enum jbvType type)
3638
0
{
3639
  /* Scalars should be always extracted during jsonpath execution. */
3640
0
  Assert(scalar->type != jbvBinary ||
3641
0
       !JsonContainerIsScalar(scalar->val.binary.data));
3642
3643
0
  return scalar->type == type ? scalar : NULL;
3644
0
}
3645
3646
/* Construct a JSON array from the item list */
3647
static JsonbValue *
3648
wrapItemsInArray(const JsonValueList *items)
3649
0
{
3650
0
  JsonbParseState *ps = NULL;
3651
0
  JsonValueListIterator it;
3652
0
  JsonbValue *jbv;
3653
3654
0
  pushJsonbValue(&ps, WJB_BEGIN_ARRAY, NULL);
3655
3656
0
  JsonValueListInitIterator(items, &it);
3657
0
  while ((jbv = JsonValueListNext(items, &it)))
3658
0
    pushJsonbValue(&ps, WJB_ELEM, jbv);
3659
3660
0
  return pushJsonbValue(&ps, WJB_END_ARRAY, NULL);
3661
0
}
3662
3663
/* Check if the timezone required for casting from type1 to type2 is used */
3664
static void
3665
checkTimezoneIsUsedForCast(bool useTz, const char *type1, const char *type2)
3666
0
{
3667
0
  if (!useTz)
3668
0
    ereport(ERROR,
3669
0
        (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3670
0
         errmsg("cannot convert value from %s to %s without time zone usage",
3671
0
            type1, type2),
3672
0
         errhint("Use *_tz() function for time zone support.")));
3673
0
}
3674
3675
/* Convert time datum to timetz datum */
3676
static Datum
3677
castTimeToTimeTz(Datum time, bool useTz)
3678
0
{
3679
0
  checkTimezoneIsUsedForCast(useTz, "time", "timetz");
3680
3681
0
  return DirectFunctionCall1(time_timetz, time);
3682
0
}
3683
3684
/*
3685
 * Compare date to timestamp.
3686
 * Note that this doesn't involve any timezone considerations.
3687
 */
3688
static int
3689
cmpDateToTimestamp(DateADT date1, Timestamp ts2, bool useTz)
3690
0
{
3691
0
  return date_cmp_timestamp_internal(date1, ts2);
3692
0
}
3693
3694
/*
3695
 * Compare date to timestamptz.
3696
 */
3697
static int
3698
cmpDateToTimestampTz(DateADT date1, TimestampTz tstz2, bool useTz)
3699
0
{
3700
0
  checkTimezoneIsUsedForCast(useTz, "date", "timestamptz");
3701
3702
0
  return date_cmp_timestamptz_internal(date1, tstz2);
3703
0
}
3704
3705
/*
3706
 * Compare timestamp to timestamptz.
3707
 */
3708
static int
3709
cmpTimestampToTimestampTz(Timestamp ts1, TimestampTz tstz2, bool useTz)
3710
0
{
3711
0
  checkTimezoneIsUsedForCast(useTz, "timestamp", "timestamptz");
3712
3713
0
  return timestamp_cmp_timestamptz_internal(ts1, tstz2);
3714
0
}
3715
3716
/*
3717
 * Cross-type comparison of two datetime SQL/JSON items.  If items are
3718
 * uncomparable *cast_error flag is set, otherwise *cast_error is unset.
3719
 * If the cast requires timezone and it is not used, then explicit error is thrown.
3720
 */
3721
static int
3722
compareDatetime(Datum val1, Oid typid1, Datum val2, Oid typid2,
3723
        bool useTz, bool *cast_error)
3724
0
{
3725
0
  PGFunction  cmpfunc;
3726
3727
0
  *cast_error = false;
3728
3729
0
  switch (typid1)
3730
0
  {
3731
0
    case DATEOID:
3732
0
      switch (typid2)
3733
0
      {
3734
0
        case DATEOID:
3735
0
          cmpfunc = date_cmp;
3736
3737
0
          break;
3738
3739
0
        case TIMESTAMPOID:
3740
0
          return cmpDateToTimestamp(DatumGetDateADT(val1),
3741
0
                        DatumGetTimestamp(val2),
3742
0
                        useTz);
3743
3744
0
        case TIMESTAMPTZOID:
3745
0
          return cmpDateToTimestampTz(DatumGetDateADT(val1),
3746
0
                        DatumGetTimestampTz(val2),
3747
0
                        useTz);
3748
3749
0
        case TIMEOID:
3750
0
        case TIMETZOID:
3751
0
          *cast_error = true; /* uncomparable types */
3752
0
          return 0;
3753
3754
0
        default:
3755
0
          elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u",
3756
0
             typid2);
3757
0
      }
3758
0
      break;
3759
3760
0
    case TIMEOID:
3761
0
      switch (typid2)
3762
0
      {
3763
0
        case TIMEOID:
3764
0
          cmpfunc = time_cmp;
3765
3766
0
          break;
3767
3768
0
        case TIMETZOID:
3769
0
          val1 = castTimeToTimeTz(val1, useTz);
3770
0
          cmpfunc = timetz_cmp;
3771
3772
0
          break;
3773
3774
0
        case DATEOID:
3775
0
        case TIMESTAMPOID:
3776
0
        case TIMESTAMPTZOID:
3777
0
          *cast_error = true; /* uncomparable types */
3778
0
          return 0;
3779
3780
0
        default:
3781
0
          elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u",
3782
0
             typid2);
3783
0
      }
3784
0
      break;
3785
3786
0
    case TIMETZOID:
3787
0
      switch (typid2)
3788
0
      {
3789
0
        case TIMEOID:
3790
0
          val2 = castTimeToTimeTz(val2, useTz);
3791
0
          cmpfunc = timetz_cmp;
3792
3793
0
          break;
3794
3795
0
        case TIMETZOID:
3796
0
          cmpfunc = timetz_cmp;
3797
3798
0
          break;
3799
3800
0
        case DATEOID:
3801
0
        case TIMESTAMPOID:
3802
0
        case TIMESTAMPTZOID:
3803
0
          *cast_error = true; /* uncomparable types */
3804
0
          return 0;
3805
3806
0
        default:
3807
0
          elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u",
3808
0
             typid2);
3809
0
      }
3810
0
      break;
3811
3812
0
    case TIMESTAMPOID:
3813
0
      switch (typid2)
3814
0
      {
3815
0
        case DATEOID:
3816
0
          return -cmpDateToTimestamp(DatumGetDateADT(val2),
3817
0
                         DatumGetTimestamp(val1),
3818
0
                         useTz);
3819
3820
0
        case TIMESTAMPOID:
3821
0
          cmpfunc = timestamp_cmp;
3822
3823
0
          break;
3824
3825
0
        case TIMESTAMPTZOID:
3826
0
          return cmpTimestampToTimestampTz(DatumGetTimestamp(val1),
3827
0
                           DatumGetTimestampTz(val2),
3828
0
                           useTz);
3829
3830
0
        case TIMEOID:
3831
0
        case TIMETZOID:
3832
0
          *cast_error = true; /* uncomparable types */
3833
0
          return 0;
3834
3835
0
        default:
3836
0
          elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u",
3837
0
             typid2);
3838
0
      }
3839
0
      break;
3840
3841
0
    case TIMESTAMPTZOID:
3842
0
      switch (typid2)
3843
0
      {
3844
0
        case DATEOID:
3845
0
          return -cmpDateToTimestampTz(DatumGetDateADT(val2),
3846
0
                         DatumGetTimestampTz(val1),
3847
0
                         useTz);
3848
3849
0
        case TIMESTAMPOID:
3850
0
          return -cmpTimestampToTimestampTz(DatumGetTimestamp(val2),
3851
0
                            DatumGetTimestampTz(val1),
3852
0
                            useTz);
3853
3854
0
        case TIMESTAMPTZOID:
3855
0
          cmpfunc = timestamp_cmp;
3856
3857
0
          break;
3858
3859
0
        case TIMEOID:
3860
0
        case TIMETZOID:
3861
0
          *cast_error = true; /* uncomparable types */
3862
0
          return 0;
3863
3864
0
        default:
3865
0
          elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u",
3866
0
             typid2);
3867
0
      }
3868
0
      break;
3869
3870
0
    default:
3871
0
      elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u", typid1);
3872
0
  }
3873
3874
0
  if (*cast_error)
3875
0
    return 0;       /* cast error */
3876
3877
0
  return DatumGetInt32(DirectFunctionCall2(cmpfunc, val1, val2));
3878
0
}
3879
3880
/*
3881
 * Executor-callable JSON_EXISTS implementation
3882
 *
3883
 * Returns NULL instead of throwing errors if 'error' is not NULL, setting
3884
 * *error to true.
3885
 */
3886
bool
3887
JsonPathExists(Datum jb, JsonPath *jp, bool *error, List *vars)
3888
0
{
3889
0
  JsonPathExecResult res;
3890
3891
0
  res = executeJsonPath(jp, vars,
3892
0
              GetJsonPathVar, CountJsonPathVars,
3893
0
              DatumGetJsonbP(jb), !error, NULL, true);
3894
3895
0
  Assert(error || !jperIsError(res));
3896
3897
0
  if (error && jperIsError(res))
3898
0
    *error = true;
3899
3900
0
  return res == jperOk;
3901
0
}
3902
3903
/*
3904
 * Executor-callable JSON_QUERY implementation
3905
 *
3906
 * Returns NULL instead of throwing errors if 'error' is not NULL, setting
3907
 * *error to true.  *empty is set to true if no match is found.
3908
 */
3909
Datum
3910
JsonPathQuery(Datum jb, JsonPath *jp, JsonWrapper wrapper, bool *empty,
3911
        bool *error, List *vars,
3912
        const char *column_name)
3913
0
{
3914
0
  JsonbValue *singleton;
3915
0
  bool    wrap;
3916
0
  JsonValueList found = {0};
3917
0
  JsonPathExecResult res;
3918
0
  int     count;
3919
3920
0
  res = executeJsonPath(jp, vars,
3921
0
              GetJsonPathVar, CountJsonPathVars,
3922
0
              DatumGetJsonbP(jb), !error, &found, true);
3923
0
  Assert(error || !jperIsError(res));
3924
0
  if (error && jperIsError(res))
3925
0
  {
3926
0
    *error = true;
3927
0
    *empty = false;
3928
0
    return (Datum) 0;
3929
0
  }
3930
3931
  /*
3932
   * Determine whether to wrap the result in a JSON array or not.
3933
   *
3934
   * First, count the number of SQL/JSON items in the returned
3935
   * JsonValueList. If the list is empty (singleton == NULL), no wrapping is
3936
   * necessary.
3937
   *
3938
   * If the wrapper mode is JSW_NONE or JSW_UNSPEC, wrapping is explicitly
3939
   * disabled. This enforces a WITHOUT WRAPPER clause, which is also the
3940
   * default when no WRAPPER clause is specified.
3941
   *
3942
   * If the mode is JSW_UNCONDITIONAL, wrapping is enforced regardless of
3943
   * the number of SQL/JSON items, enforcing a WITH WRAPPER or WITH
3944
   * UNCONDITIONAL WRAPPER clause.
3945
   *
3946
   * For JSW_CONDITIONAL, wrapping occurs only if there is more than one
3947
   * SQL/JSON item in the list, enforcing a WITH CONDITIONAL WRAPPER clause.
3948
   */
3949
0
  count = JsonValueListLength(&found);
3950
0
  singleton = count > 0 ? JsonValueListHead(&found) : NULL;
3951
0
  if (singleton == NULL)
3952
0
    wrap = false;
3953
0
  else if (wrapper == JSW_NONE || wrapper == JSW_UNSPEC)
3954
0
    wrap = false;
3955
0
  else if (wrapper == JSW_UNCONDITIONAL)
3956
0
    wrap = true;
3957
0
  else if (wrapper == JSW_CONDITIONAL)
3958
0
    wrap = count > 1;
3959
0
  else
3960
0
  {
3961
0
    elog(ERROR, "unrecognized json wrapper %d", (int) wrapper);
3962
0
    wrap = false;
3963
0
  }
3964
3965
0
  if (wrap)
3966
0
    return JsonbPGetDatum(JsonbValueToJsonb(wrapItemsInArray(&found)));
3967
3968
  /* No wrapping means only one item is expected. */
3969
0
  if (count > 1)
3970
0
  {
3971
0
    if (error)
3972
0
    {
3973
0
      *error = true;
3974
0
      return (Datum) 0;
3975
0
    }
3976
3977
0
    if (column_name)
3978
0
      ereport(ERROR,
3979
0
          (errcode(ERRCODE_MORE_THAN_ONE_SQL_JSON_ITEM),
3980
0
           errmsg("JSON path expression for column \"%s\" must return single item when no wrapper is requested",
3981
0
              column_name),
3982
0
           errhint("Use the WITH WRAPPER clause to wrap SQL/JSON items into an array.")));
3983
0
    else
3984
0
      ereport(ERROR,
3985
0
          (errcode(ERRCODE_MORE_THAN_ONE_SQL_JSON_ITEM),
3986
0
           errmsg("JSON path expression in JSON_QUERY must return single item when no wrapper is requested"),
3987
0
           errhint("Use the WITH WRAPPER clause to wrap SQL/JSON items into an array.")));
3988
0
  }
3989
3990
0
  if (singleton)
3991
0
    return JsonbPGetDatum(JsonbValueToJsonb(singleton));
3992
3993
0
  *empty = true;
3994
0
  return PointerGetDatum(NULL);
3995
0
}
3996
3997
/*
3998
 * Executor-callable JSON_VALUE implementation
3999
 *
4000
 * Returns NULL instead of throwing errors if 'error' is not NULL, setting
4001
 * *error to true.  *empty is set to true if no match is found.
4002
 */
4003
JsonbValue *
4004
JsonPathValue(Datum jb, JsonPath *jp, bool *empty, bool *error, List *vars,
4005
        const char *column_name)
4006
0
{
4007
0
  JsonbValue *res;
4008
0
  JsonValueList found = {0};
4009
0
  JsonPathExecResult jper PG_USED_FOR_ASSERTS_ONLY;
4010
0
  int     count;
4011
4012
0
  jper = executeJsonPath(jp, vars, GetJsonPathVar, CountJsonPathVars,
4013
0
               DatumGetJsonbP(jb),
4014
0
               !error, &found, true);
4015
4016
0
  Assert(error || !jperIsError(jper));
4017
4018
0
  if (error && jperIsError(jper))
4019
0
  {
4020
0
    *error = true;
4021
0
    *empty = false;
4022
0
    return NULL;
4023
0
  }
4024
4025
0
  count = JsonValueListLength(&found);
4026
4027
0
  *empty = (count == 0);
4028
4029
0
  if (*empty)
4030
0
    return NULL;
4031
4032
  /* JSON_VALUE expects to get only singletons. */
4033
0
  if (count > 1)
4034
0
  {
4035
0
    if (error)
4036
0
    {
4037
0
      *error = true;
4038
0
      return NULL;
4039
0
    }
4040
4041
0
    if (column_name)
4042
0
      ereport(ERROR,
4043
0
          (errcode(ERRCODE_MORE_THAN_ONE_SQL_JSON_ITEM),
4044
0
           errmsg("JSON path expression for column \"%s\" must return single scalar item",
4045
0
              column_name)));
4046
0
    else
4047
0
      ereport(ERROR,
4048
0
          (errcode(ERRCODE_MORE_THAN_ONE_SQL_JSON_ITEM),
4049
0
           errmsg("JSON path expression in JSON_VALUE must return single scalar item")));
4050
0
  }
4051
4052
0
  res = JsonValueListHead(&found);
4053
0
  if (res->type == jbvBinary && JsonContainerIsScalar(res->val.binary.data))
4054
0
    JsonbExtractScalar(res->val.binary.data, res);
4055
4056
  /* JSON_VALUE expects to get only scalars. */
4057
0
  if (!IsAJsonbScalar(res))
4058
0
  {
4059
0
    if (error)
4060
0
    {
4061
0
      *error = true;
4062
0
      return NULL;
4063
0
    }
4064
4065
0
    if (column_name)
4066
0
      ereport(ERROR,
4067
0
          (errcode(ERRCODE_SQL_JSON_SCALAR_REQUIRED),
4068
0
           errmsg("JSON path expression for column \"%s\" must return single scalar item",
4069
0
              column_name)));
4070
0
    else
4071
0
      ereport(ERROR,
4072
0
          (errcode(ERRCODE_SQL_JSON_SCALAR_REQUIRED),
4073
0
           errmsg("JSON path expression in JSON_VALUE must return single scalar item")));
4074
0
  }
4075
4076
0
  if (res->type == jbvNull)
4077
0
    return NULL;
4078
4079
0
  return res;
4080
0
}
4081
4082
/************************ JSON_TABLE functions ***************************/
4083
4084
/*
4085
 * Sanity-checks and returns the opaque JsonTableExecContext from the
4086
 * given executor state struct.
4087
 */
4088
static inline JsonTableExecContext *
4089
GetJsonTableExecContext(TableFuncScanState *state, const char *fname)
4090
0
{
4091
0
  JsonTableExecContext *result;
4092
4093
0
  if (!IsA(state, TableFuncScanState))
4094
0
    elog(ERROR, "%s called with invalid TableFuncScanState", fname);
4095
0
  result = (JsonTableExecContext *) state->opaque;
4096
0
  if (result->magic != JSON_TABLE_EXEC_CONTEXT_MAGIC)
4097
0
    elog(ERROR, "%s called with invalid TableFuncScanState", fname);
4098
4099
0
  return result;
4100
0
}
4101
4102
/*
4103
 * JsonTableInitOpaque
4104
 *    Fill in TableFuncScanState->opaque for processing JSON_TABLE
4105
 *
4106
 * This initializes the PASSING arguments and the JsonTablePlanState for
4107
 * JsonTablePlan given in TableFunc.
4108
 */
4109
static void
4110
JsonTableInitOpaque(TableFuncScanState *state, int natts)
4111
0
{
4112
0
  JsonTableExecContext *cxt;
4113
0
  PlanState  *ps = &state->ss.ps;
4114
0
  TableFuncScan *tfs = castNode(TableFuncScan, ps->plan);
4115
0
  TableFunc  *tf = tfs->tablefunc;
4116
0
  JsonTablePlan *rootplan = (JsonTablePlan *) tf->plan;
4117
0
  JsonExpr   *je = castNode(JsonExpr, tf->docexpr);
4118
0
  List     *args = NIL;
4119
4120
0
  cxt = palloc0(sizeof(JsonTableExecContext));
4121
0
  cxt->magic = JSON_TABLE_EXEC_CONTEXT_MAGIC;
4122
4123
  /*
4124
   * Evaluate JSON_TABLE() PASSING arguments to be passed to the jsonpath
4125
   * executor via JsonPathVariables.
4126
   */
4127
0
  if (state->passingvalexprs)
4128
0
  {
4129
0
    ListCell   *exprlc;
4130
0
    ListCell   *namelc;
4131
4132
0
    Assert(list_length(state->passingvalexprs) ==
4133
0
         list_length(je->passing_names));
4134
0
    forboth(exprlc, state->passingvalexprs,
4135
0
        namelc, je->passing_names)
4136
0
    {
4137
0
      ExprState  *state = lfirst_node(ExprState, exprlc);
4138
0
      String     *name = lfirst_node(String, namelc);
4139
0
      JsonPathVariable *var = palloc(sizeof(*var));
4140
4141
0
      var->name = pstrdup(name->sval);
4142
0
      var->namelen = strlen(var->name);
4143
0
      var->typid = exprType((Node *) state->expr);
4144
0
      var->typmod = exprTypmod((Node *) state->expr);
4145
4146
      /*
4147
       * Evaluate the expression and save the value to be returned by
4148
       * GetJsonPathVar().
4149
       */
4150
0
      var->value = ExecEvalExpr(state, ps->ps_ExprContext,
4151
0
                    &var->isnull);
4152
4153
0
      args = lappend(args, var);
4154
0
    }
4155
0
  }
4156
4157
0
  cxt->colplanstates = palloc(sizeof(JsonTablePlanState *) *
4158
0
                list_length(tf->colvalexprs));
4159
4160
  /*
4161
   * Initialize plan for the root path and, recursively, also any child
4162
   * plans that compute the NESTED paths.
4163
   */
4164
0
  cxt->rootplanstate = JsonTableInitPlan(cxt, rootplan, NULL, args,
4165
0
                       CurrentMemoryContext);
4166
4167
0
  state->opaque = cxt;
4168
0
}
4169
4170
/*
4171
 * JsonTableDestroyOpaque
4172
 *    Resets state->opaque
4173
 */
4174
static void
4175
JsonTableDestroyOpaque(TableFuncScanState *state)
4176
0
{
4177
0
  JsonTableExecContext *cxt =
4178
0
    GetJsonTableExecContext(state, "JsonTableDestroyOpaque");
4179
4180
  /* not valid anymore */
4181
0
  cxt->magic = 0;
4182
4183
0
  state->opaque = NULL;
4184
0
}
4185
4186
/*
4187
 * JsonTableInitPlan
4188
 *    Initialize information for evaluating jsonpath in the given
4189
 *    JsonTablePlan and, recursively, in any child plans
4190
 */
4191
static JsonTablePlanState *
4192
JsonTableInitPlan(JsonTableExecContext *cxt, JsonTablePlan *plan,
4193
          JsonTablePlanState *parentstate,
4194
          List *args, MemoryContext mcxt)
4195
0
{
4196
0
  JsonTablePlanState *planstate = palloc0(sizeof(*planstate));
4197
4198
0
  planstate->plan = plan;
4199
0
  planstate->parent = parentstate;
4200
4201
0
  if (IsA(plan, JsonTablePathScan))
4202
0
  {
4203
0
    JsonTablePathScan *scan = (JsonTablePathScan *) plan;
4204
0
    int     i;
4205
4206
0
    planstate->path = DatumGetJsonPathP(scan->path->value->constvalue);
4207
0
    planstate->args = args;
4208
0
    planstate->mcxt = AllocSetContextCreate(mcxt, "JsonTableExecContext",
4209
0
                        ALLOCSET_DEFAULT_SIZES);
4210
4211
    /* No row pattern evaluated yet. */
4212
0
    planstate->current.value = PointerGetDatum(NULL);
4213
0
    planstate->current.isnull = true;
4214
4215
0
    for (i = scan->colMin; i >= 0 && i <= scan->colMax; i++)
4216
0
      cxt->colplanstates[i] = planstate;
4217
4218
0
    planstate->nested = scan->child ?
4219
0
      JsonTableInitPlan(cxt, scan->child, planstate, args, mcxt) : NULL;
4220
0
  }
4221
0
  else if (IsA(plan, JsonTableSiblingJoin))
4222
0
  {
4223
0
    JsonTableSiblingJoin *join = (JsonTableSiblingJoin *) plan;
4224
4225
0
    planstate->left = JsonTableInitPlan(cxt, join->lplan, parentstate,
4226
0
                      args, mcxt);
4227
0
    planstate->right = JsonTableInitPlan(cxt, join->rplan, parentstate,
4228
0
                       args, mcxt);
4229
0
  }
4230
4231
0
  return planstate;
4232
0
}
4233
4234
/*
4235
 * JsonTableSetDocument
4236
 *    Install the input document and evaluate the row pattern
4237
 */
4238
static void
4239
JsonTableSetDocument(TableFuncScanState *state, Datum value)
4240
0
{
4241
0
  JsonTableExecContext *cxt =
4242
0
    GetJsonTableExecContext(state, "JsonTableSetDocument");
4243
4244
0
  JsonTableResetRowPattern(cxt->rootplanstate, value);
4245
0
}
4246
4247
/*
4248
 * Evaluate a JsonTablePlan's jsonpath to get a new row pattern from
4249
 * the given context item
4250
 */
4251
static void
4252
JsonTableResetRowPattern(JsonTablePlanState *planstate, Datum item)
4253
0
{
4254
0
  JsonTablePathScan *scan = castNode(JsonTablePathScan, planstate->plan);
4255
0
  MemoryContext oldcxt;
4256
0
  JsonPathExecResult res;
4257
0
  Jsonb    *js = (Jsonb *) DatumGetJsonbP(item);
4258
4259
0
  JsonValueListClear(&planstate->found);
4260
4261
0
  MemoryContextResetOnly(planstate->mcxt);
4262
4263
0
  oldcxt = MemoryContextSwitchTo(planstate->mcxt);
4264
4265
0
  res = executeJsonPath(planstate->path, planstate->args,
4266
0
              GetJsonPathVar, CountJsonPathVars,
4267
0
              js, scan->errorOnError,
4268
0
              &planstate->found,
4269
0
              true);
4270
4271
0
  MemoryContextSwitchTo(oldcxt);
4272
4273
0
  if (jperIsError(res))
4274
0
  {
4275
0
    Assert(!scan->errorOnError);
4276
0
    JsonValueListClear(&planstate->found);
4277
0
  }
4278
4279
  /* Reset plan iterator to the beginning of the item list */
4280
0
  JsonValueListInitIterator(&planstate->found, &planstate->iter);
4281
0
  planstate->current.value = PointerGetDatum(NULL);
4282
0
  planstate->current.isnull = true;
4283
0
  planstate->ordinal = 0;
4284
0
}
4285
4286
/*
4287
 * Fetch next row from a JsonTablePlan.
4288
 *
4289
 * Returns false if the plan has run out of rows, true otherwise.
4290
 */
4291
static bool
4292
JsonTablePlanNextRow(JsonTablePlanState *planstate)
4293
0
{
4294
0
  if (IsA(planstate->plan, JsonTablePathScan))
4295
0
    return JsonTablePlanScanNextRow(planstate);
4296
0
  else if (IsA(planstate->plan, JsonTableSiblingJoin))
4297
0
    return JsonTablePlanJoinNextRow(planstate);
4298
0
  else
4299
0
    elog(ERROR, "invalid JsonTablePlan %d", (int) planstate->plan->type);
4300
4301
0
  Assert(false);
4302
  /* Appease compiler */
4303
0
  return false;
4304
0
}
4305
4306
/*
4307
 * Fetch next row from a JsonTablePlan's path evaluation result and from
4308
 * any child nested path(s).
4309
 *
4310
 * Returns true if any of the paths (this or the nested) has more rows to
4311
 * return.
4312
 *
4313
 * By fetching the nested path(s)'s rows based on the parent row at each
4314
 * level, this essentially joins the rows of different levels.  If a nested
4315
 * path at a given level has no matching rows, the columns of that level will
4316
 * compute to NULL, making it an OUTER join.
4317
 */
4318
static bool
4319
JsonTablePlanScanNextRow(JsonTablePlanState *planstate)
4320
0
{
4321
0
  JsonbValue *jbv;
4322
0
  MemoryContext oldcxt;
4323
4324
  /*
4325
   * If planstate already has an active row and there is a nested plan,
4326
   * check if it has an active row to join with the former.
4327
   */
4328
0
  if (!planstate->current.isnull)
4329
0
  {
4330
0
    if (planstate->nested && JsonTablePlanNextRow(planstate->nested))
4331
0
      return true;
4332
0
  }
4333
4334
  /* Fetch new row from the list of found values to set as active. */
4335
0
  jbv = JsonValueListNext(&planstate->found, &planstate->iter);
4336
4337
  /* End of list? */
4338
0
  if (jbv == NULL)
4339
0
  {
4340
0
    planstate->current.value = PointerGetDatum(NULL);
4341
0
    planstate->current.isnull = true;
4342
0
    return false;
4343
0
  }
4344
4345
  /*
4346
   * Set current row item for subsequent JsonTableGetValue() calls for
4347
   * evaluating individual columns.
4348
   */
4349
0
  oldcxt = MemoryContextSwitchTo(planstate->mcxt);
4350
0
  planstate->current.value = JsonbPGetDatum(JsonbValueToJsonb(jbv));
4351
0
  planstate->current.isnull = false;
4352
0
  MemoryContextSwitchTo(oldcxt);
4353
4354
  /* Next row! */
4355
0
  planstate->ordinal++;
4356
4357
  /* Process nested plan(s), if any. */
4358
0
  if (planstate->nested)
4359
0
  {
4360
    /* Re-evaluate the nested path using the above parent row. */
4361
0
    JsonTableResetNestedPlan(planstate->nested);
4362
4363
    /*
4364
     * Now fetch the nested plan's current row to be joined against the
4365
     * parent row.  Any further nested plans' paths will be re-evaluated
4366
     * recursively, level at a time, after setting each nested plan's
4367
     * current row.
4368
     */
4369
0
    (void) JsonTablePlanNextRow(planstate->nested);
4370
0
  }
4371
4372
  /* There are more rows. */
4373
0
  return true;
4374
0
}
4375
4376
/*
4377
 * Re-evaluate the row pattern of a nested plan using the new parent row
4378
 * pattern.
4379
 */
4380
static void
4381
JsonTableResetNestedPlan(JsonTablePlanState *planstate)
4382
0
{
4383
  /* This better be a child plan. */
4384
0
  Assert(planstate->parent != NULL);
4385
0
  if (IsA(planstate->plan, JsonTablePathScan))
4386
0
  {
4387
0
    JsonTablePlanState *parent = planstate->parent;
4388
4389
0
    if (!parent->current.isnull)
4390
0
      JsonTableResetRowPattern(planstate, parent->current.value);
4391
4392
    /*
4393
     * If this plan itself has a child nested plan, it will be reset when
4394
     * the caller calls JsonTablePlanNextRow() on this plan.
4395
     */
4396
0
  }
4397
0
  else if (IsA(planstate->plan, JsonTableSiblingJoin))
4398
0
  {
4399
0
    JsonTableResetNestedPlan(planstate->left);
4400
0
    JsonTableResetNestedPlan(planstate->right);
4401
0
  }
4402
0
}
4403
4404
/*
4405
 * Fetch the next row from a JsonTableSiblingJoin.
4406
 *
4407
 * This is essentially a UNION between the rows from left and right siblings.
4408
 */
4409
static bool
4410
JsonTablePlanJoinNextRow(JsonTablePlanState *planstate)
4411
0
{
4412
4413
  /* Fetch row from left sibling. */
4414
0
  if (!JsonTablePlanNextRow(planstate->left))
4415
0
  {
4416
    /*
4417
     * Left sibling ran out of rows, so start fetching from the right
4418
     * sibling.
4419
     */
4420
0
    if (!JsonTablePlanNextRow(planstate->right))
4421
0
    {
4422
      /* Right sibling ran out of row, so there are more rows. */
4423
0
      return false;
4424
0
    }
4425
0
  }
4426
4427
0
  return true;
4428
0
}
4429
4430
/*
4431
 * JsonTableFetchRow
4432
 *    Prepare the next "current" row for upcoming GetValue calls.
4433
 *
4434
 * Returns false if no more rows can be returned.
4435
 */
4436
static bool
4437
JsonTableFetchRow(TableFuncScanState *state)
4438
0
{
4439
0
  JsonTableExecContext *cxt =
4440
0
    GetJsonTableExecContext(state, "JsonTableFetchRow");
4441
4442
0
  return JsonTablePlanNextRow(cxt->rootplanstate);
4443
0
}
4444
4445
/*
4446
 * JsonTableGetValue
4447
 *    Return the value for column number 'colnum' for the current row.
4448
 *
4449
 * This leaks memory, so be sure to reset often the context in which it's
4450
 * called.
4451
 */
4452
static Datum
4453
JsonTableGetValue(TableFuncScanState *state, int colnum,
4454
          Oid typid, int32 typmod, bool *isnull)
4455
0
{
4456
0
  JsonTableExecContext *cxt =
4457
0
    GetJsonTableExecContext(state, "JsonTableGetValue");
4458
0
  ExprContext *econtext = state->ss.ps.ps_ExprContext;
4459
0
  ExprState  *estate = list_nth(state->colvalexprs, colnum);
4460
0
  JsonTablePlanState *planstate = cxt->colplanstates[colnum];
4461
0
  JsonTablePlanRowSource *current = &planstate->current;
4462
0
  Datum   result;
4463
4464
  /* Row pattern value is NULL */
4465
0
  if (current->isnull)
4466
0
  {
4467
0
    result = (Datum) 0;
4468
0
    *isnull = true;
4469
0
  }
4470
  /* Evaluate JsonExpr. */
4471
0
  else if (estate)
4472
0
  {
4473
0
    Datum   saved_caseValue = econtext->caseValue_datum;
4474
0
    bool    saved_caseIsNull = econtext->caseValue_isNull;
4475
4476
    /* Pass the row pattern value via CaseTestExpr. */
4477
0
    econtext->caseValue_datum = current->value;
4478
0
    econtext->caseValue_isNull = false;
4479
4480
0
    result = ExecEvalExpr(estate, econtext, isnull);
4481
4482
0
    econtext->caseValue_datum = saved_caseValue;
4483
0
    econtext->caseValue_isNull = saved_caseIsNull;
4484
0
  }
4485
  /* ORDINAL column */
4486
0
  else
4487
0
  {
4488
0
    result = Int32GetDatum(planstate->ordinal);
4489
0
    *isnull = false;
4490
0
  }
4491
4492
0
  return result;
4493
0
}