PostgreSQL Source Code  git master
jsonpath.c
Go to the documentation of this file.
1 /*-------------------------------------------------------------------------
2  *
3  * jsonpath.c
4  * Input/output and supporting routines for jsonpath
5  *
6  * jsonpath expression is a chain of path items. First path item is $, $var,
7  * literal or arithmetic expression. Subsequent path items are accessors
8  * (.key, .*, [subscripts], [*]), filters (? (predicate)) and methods (.type(),
9  * .size() etc).
10  *
11  * For instance, structure of path items for simple expression:
12  *
13  * $.a[*].type()
14  *
15  * is pretty evident:
16  *
17  * $ => .a => [*] => .type()
18  *
19  * Some path items such as arithmetic operations, predicates or array
20  * subscripts may comprise subtrees. For instance, more complex expression
21  *
22  * ($.a + $[1 to 5, 7] ? (@ > 3).double()).type()
23  *
24  * have following structure of path items:
25  *
26  * + => .type()
27  * ___/ \___
28  * / \
29  * $ => .a $ => [] => ? => .double()
30  * _||_ |
31  * / \ >
32  * to to / \
33  * / \ / @ 3
34  * 1 5 7
35  *
36  * Binary encoding of jsonpath constitutes a sequence of 4-bytes aligned
37  * variable-length path items connected by links. Every item has a header
38  * consisting of item type (enum JsonPathItemType) and offset of next item
39  * (zero means no next item). After the header, item may have payload
40  * depending on item type. For instance, payload of '.key' accessor item is
41  * length of key name and key name itself. Payload of '>' arithmetic operator
42  * item is offsets of right and left operands.
43  *
44  * So, binary representation of sample expression above is:
45  * (bottom arrows are next links, top lines are argument links)
46  *
47  * _____
48  * _____ ___/____ \ __
49  * _ /_ \ _____/__/____ \ \ __ _ /_ \
50  * / / \ \ / / / \ \ \ / \ / / \ \
51  * +(LR) $ .a $ [](* to *, * to *) 1 5 7 ?(A) >(LR) @ 3 .double() .type()
52  * | | ^ | ^| ^| ^ ^
53  * | |__| |__||________________________||___________________| |
54  * |_______________________________________________________________________|
55  *
56  * Copyright (c) 2019-2022, PostgreSQL Global Development Group
57  *
58  * IDENTIFICATION
59  * src/backend/utils/adt/jsonpath.c
60  *
61  *-------------------------------------------------------------------------
62  */
63 
64 #include "postgres.h"
65 
66 #include "funcapi.h"
67 #include "lib/stringinfo.h"
68 #include "libpq/pqformat.h"
69 #include "miscadmin.h"
70 #include "nodes/nodeFuncs.h"
71 #include "utils/builtins.h"
72 #include "utils/formatting.h"
73 #include "utils/json.h"
74 #include "utils/jsonpath.h"
75 
76 
77 static Datum jsonPathFromCstring(char *in, int len);
78 static char *jsonPathToCstring(StringInfo out, JsonPath *in,
79  int estimated_len);
81  int nestingLevel, bool insideArraySubscript);
82 static void alignStringInfoInt(StringInfo buf);
84 static void printJsonPathItem(StringInfo buf, JsonPathItem *v, bool inKey,
85  bool printBracketes);
86 static int operationPriority(JsonPathItemType op);
87 
88 
89 /**************************** INPUT/OUTPUT ********************************/
90 
91 /*
92  * jsonpath type input function
93  */
94 Datum
96 {
97  char *in = PG_GETARG_CSTRING(0);
98  int len = strlen(in);
99 
100  return jsonPathFromCstring(in, len);
101 }
102 
103 /*
104  * jsonpath type recv function
105  *
106  * The type is sent as text in binary mode, so this is almost the same
107  * as the input function, but it's prefixed with a version number so we
108  * can change the binary format sent in future if necessary. For now,
109  * only version 1 is supported.
110  */
111 Datum
113 {
115  int version = pq_getmsgint(buf, 1);
116  char *str;
117  int nbytes;
118 
119  if (version == JSONPATH_VERSION)
120  str = pq_getmsgtext(buf, buf->len - buf->cursor, &nbytes);
121  else
122  elog(ERROR, "unsupported jsonpath version number: %d", version);
123 
124  return jsonPathFromCstring(str, nbytes);
125 }
126 
127 /*
128  * jsonpath type output function
129  */
130 Datum
132 {
134 
136 }
137 
138 /*
139  * jsonpath type send function
140  *
141  * Just send jsonpath as a version number, then a string of text
142  */
143 Datum
145 {
148  StringInfoData jtext;
149  int version = JSONPATH_VERSION;
150 
151  initStringInfo(&jtext);
152  (void) jsonPathToCstring(&jtext, in, VARSIZE(in));
153 
155  pq_sendint8(&buf, version);
156  pq_sendtext(&buf, jtext.data, jtext.len);
157  pfree(jtext.data);
158 
160 }
161 
162 /*
163  * Converts C-string to a jsonpath value.
164  *
165  * Uses jsonpath parser to turn string into an AST, then
166  * flattenJsonPathParseItem() does second pass turning AST into binary
167  * representation of jsonpath.
168  */
169 static Datum
170 jsonPathFromCstring(char *in, int len)
171 {
172  JsonPathParseResult *jsonpath = parsejsonpath(in, len);
173  JsonPath *res;
175 
177  enlargeStringInfo(&buf, 4 * len /* estimation */ );
178 
180 
181  if (!jsonpath)
182  ereport(ERROR,
183  (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
184  errmsg("invalid input syntax for type %s: \"%s\"", "jsonpath",
185  in)));
186 
187  flattenJsonPathParseItem(&buf, jsonpath->expr, 0, false);
188 
189  res = (JsonPath *) buf.data;
190  SET_VARSIZE(res, buf.len);
191  res->header = JSONPATH_VERSION;
192  if (jsonpath->lax)
193  res->header |= JSONPATH_LAX;
194 
196 }
197 
198 /*
199  * Converts jsonpath value to a C-string.
200  *
201  * If 'out' argument is non-null, the resulting C-string is stored inside the
202  * StringBuffer. The resulting string is always returned.
203  */
204 static char *
205 jsonPathToCstring(StringInfo out, JsonPath *in, int estimated_len)
206 {
208  JsonPathItem v;
209 
210  if (!out)
211  {
212  out = &buf;
213  initStringInfo(out);
214  }
215  enlargeStringInfo(out, estimated_len);
216 
217  if (!(in->header & JSONPATH_LAX))
218  appendBinaryStringInfo(out, "strict ", 7);
219 
220  jspInit(&v, in);
221  printJsonPathItem(out, &v, false, true);
222 
223  return out->data;
224 }
225 
226 /*
227  * Recursive function converting given jsonpath parse item and all its
228  * children into a binary representation.
229  */
230 static int
232  int nestingLevel, bool insideArraySubscript)
233 {
234  /* position from beginning of jsonpath data */
235  int32 pos = buf->len - JSONPATH_HDRSZ;
236  int32 chld;
237  int32 next;
238  int argNestingLevel = 0;
239 
242 
243  appendStringInfoChar(buf, (char) (item->type));
244 
245  /*
246  * We align buffer to int32 because a series of int32 values often goes
247  * after the header, and we want to read them directly by dereferencing
248  * int32 pointer (see jspInitByBuffer()).
249  */
251 
252  /*
253  * Reserve space for next item pointer. Actual value will be recorded
254  * later, after next and children items processing.
255  */
257 
258  switch (item->type)
259  {
260  case jpiString:
261  case jpiVariable:
262  case jpiKey:
263  appendBinaryStringInfo(buf, (char *) &item->value.string.len,
264  sizeof(item->value.string.len));
266  item->value.string.len);
267  appendStringInfoChar(buf, '\0');
268  break;
269  case jpiNumeric:
270  appendBinaryStringInfo(buf, (char *) item->value.numeric,
271  VARSIZE(item->value.numeric));
272  break;
273  case jpiBool:
274  appendBinaryStringInfo(buf, (char *) &item->value.boolean,
275  sizeof(item->value.boolean));
276  break;
277  case jpiAnd:
278  case jpiOr:
279  case jpiEqual:
280  case jpiNotEqual:
281  case jpiLess:
282  case jpiGreater:
283  case jpiLessOrEqual:
284  case jpiGreaterOrEqual:
285  case jpiAdd:
286  case jpiSub:
287  case jpiMul:
288  case jpiDiv:
289  case jpiMod:
290  case jpiStartsWith:
291  {
292  /*
293  * First, reserve place for left/right arg's positions, then
294  * record both args and sets actual position in reserved
295  * places.
296  */
299 
300  chld = !item->value.args.left ? pos :
302  nestingLevel + argNestingLevel,
303  insideArraySubscript);
304  *(int32 *) (buf->data + left) = chld - pos;
305 
306  chld = !item->value.args.right ? pos :
307  flattenJsonPathParseItem(buf, item->value.args.right,
308  nestingLevel + argNestingLevel,
309  insideArraySubscript);
310  *(int32 *) (buf->data + right) = chld - pos;
311  }
312  break;
313  case jpiLikeRegex:
314  {
315  int32 offs;
316 
318  (char *) &item->value.like_regex.flags,
319  sizeof(item->value.like_regex.flags));
322  (char *) &item->value.like_regex.patternlen,
323  sizeof(item->value.like_regex.patternlen));
325  item->value.like_regex.patternlen);
326  appendStringInfoChar(buf, '\0');
327 
328  chld = flattenJsonPathParseItem(buf, item->value.like_regex.expr,
329  nestingLevel,
330  insideArraySubscript);
331  *(int32 *) (buf->data + offs) = chld - pos;
332  }
333  break;
334  case jpiFilter:
335  argNestingLevel++;
336  /* FALLTHROUGH */
337  case jpiIsUnknown:
338  case jpiNot:
339  case jpiPlus:
340  case jpiMinus:
341  case jpiExists:
342  case jpiDatetime:
343  {
345 
346  chld = !item->value.arg ? pos :
348  nestingLevel + argNestingLevel,
349  insideArraySubscript);
350  *(int32 *) (buf->data + arg) = chld - pos;
351  }
352  break;
353  case jpiNull:
354  break;
355  case jpiRoot:
356  break;
357  case jpiAnyArray:
358  case jpiAnyKey:
359  break;
360  case jpiCurrent:
361  if (nestingLevel <= 0)
362  ereport(ERROR,
363  (errcode(ERRCODE_SYNTAX_ERROR),
364  errmsg("@ is not allowed in root expressions")));
365  break;
366  case jpiLast:
367  if (!insideArraySubscript)
368  ereport(ERROR,
369  (errcode(ERRCODE_SYNTAX_ERROR),
370  errmsg("LAST is allowed only in array subscripts")));
371  break;
372  case jpiIndexArray:
373  {
374  int32 nelems = item->value.array.nelems;
375  int offset;
376  int i;
377 
378  appendBinaryStringInfo(buf, (char *) &nelems, sizeof(nelems));
379 
380  offset = buf->len;
381 
382  appendStringInfoSpaces(buf, sizeof(int32) * 2 * nelems);
383 
384  for (i = 0; i < nelems; i++)
385  {
386  int32 *ppos;
387  int32 topos;
388  int32 frompos =
390  item->value.array.elems[i].from,
391  nestingLevel, true) - pos;
392 
393  if (item->value.array.elems[i].to)
395  item->value.array.elems[i].to,
396  nestingLevel, true) - pos;
397  else
398  topos = 0;
399 
400  ppos = (int32 *) &buf->data[offset + i * 2 * sizeof(int32)];
401 
402  ppos[0] = frompos;
403  ppos[1] = topos;
404  }
405  }
406  break;
407  case jpiAny:
409  (char *) &item->value.anybounds.first,
410  sizeof(item->value.anybounds.first));
412  (char *) &item->value.anybounds.last,
413  sizeof(item->value.anybounds.last));
414  break;
415  case jpiType:
416  case jpiSize:
417  case jpiAbs:
418  case jpiFloor:
419  case jpiCeiling:
420  case jpiDouble:
421  case jpiKeyValue:
422  break;
423  default:
424  elog(ERROR, "unrecognized jsonpath item type: %d", item->type);
425  }
426 
427  if (item->next)
428  {
429  chld = flattenJsonPathParseItem(buf, item->next, nestingLevel,
430  insideArraySubscript) - pos;
431  *(int32 *) (buf->data + next) = chld;
432  }
433 
434  return pos;
435 }
436 
437 /*
438  * Align StringInfo to int by adding zero padding bytes
439  */
440 static void
442 {
443  switch (INTALIGN(buf->len) - buf->len)
444  {
445  case 3:
447  /* FALLTHROUGH */
448  case 2:
450  /* FALLTHROUGH */
451  case 1:
453  /* FALLTHROUGH */
454  default:
455  break;
456  }
457 }
458 
459 /*
460  * Reserve space for int32 JsonPathItem pointer. Now zero pointer is written,
461  * actual value will be recorded at '(int32 *) &buf->data[pos]' later.
462  */
463 static int32
465 {
466  int32 pos = buf->len;
467  int32 ptr = 0;
468 
469  appendBinaryStringInfo(buf, (char *) &ptr, sizeof(ptr));
470 
471  return pos;
472 }
473 
474 /*
475  * Prints text representation of given jsonpath item and all its children.
476  */
477 static void
479  bool printBracketes)
480 {
481  JsonPathItem elem;
482  int i;
483 
486 
487  switch (v->type)
488  {
489  case jpiNull:
490  appendStringInfoString(buf, "null");
491  break;
492  case jpiKey:
493  if (inKey)
495  escape_json(buf, jspGetString(v, NULL));
496  break;
497  case jpiString:
498  escape_json(buf, jspGetString(v, NULL));
499  break;
500  case jpiVariable:
502  escape_json(buf, jspGetString(v, NULL));
503  break;
504  case jpiNumeric:
505  if (jspHasNext(v))
510  if (jspHasNext(v))
512  break;
513  case jpiBool:
514  if (jspGetBool(v))
515  appendBinaryStringInfo(buf, "true", 4);
516  else
517  appendBinaryStringInfo(buf, "false", 5);
518  break;
519  case jpiAnd:
520  case jpiOr:
521  case jpiEqual:
522  case jpiNotEqual:
523  case jpiLess:
524  case jpiGreater:
525  case jpiLessOrEqual:
526  case jpiGreaterOrEqual:
527  case jpiAdd:
528  case jpiSub:
529  case jpiMul:
530  case jpiDiv:
531  case jpiMod:
532  case jpiStartsWith:
533  if (printBracketes)
535  jspGetLeftArg(v, &elem);
536  printJsonPathItem(buf, &elem, false,
537  operationPriority(elem.type) <=
538  operationPriority(v->type));
542  jspGetRightArg(v, &elem);
543  printJsonPathItem(buf, &elem, false,
544  operationPriority(elem.type) <=
545  operationPriority(v->type));
546  if (printBracketes)
548  break;
549  case jpiLikeRegex:
550  if (printBracketes)
552 
553  jspInitByBuffer(&elem, v->base, v->content.like_regex.expr);
554  printJsonPathItem(buf, &elem, false,
555  operationPriority(elem.type) <=
556  operationPriority(v->type));
557 
558  appendBinaryStringInfo(buf, " like_regex ", 12);
559 
560  escape_json(buf, v->content.like_regex.pattern);
561 
562  if (v->content.like_regex.flags)
563  {
564  appendBinaryStringInfo(buf, " flag \"", 7);
565 
566  if (v->content.like_regex.flags & JSP_REGEX_ICASE)
568  if (v->content.like_regex.flags & JSP_REGEX_DOTALL)
570  if (v->content.like_regex.flags & JSP_REGEX_MLINE)
572  if (v->content.like_regex.flags & JSP_REGEX_WSPACE)
574  if (v->content.like_regex.flags & JSP_REGEX_QUOTE)
576 
578  }
579 
580  if (printBracketes)
582  break;
583  case jpiPlus:
584  case jpiMinus:
585  if (printBracketes)
587  appendStringInfoChar(buf, v->type == jpiPlus ? '+' : '-');
588  jspGetArg(v, &elem);
589  printJsonPathItem(buf, &elem, false,
590  operationPriority(elem.type) <=
591  operationPriority(v->type));
592  if (printBracketes)
594  break;
595  case jpiFilter:
596  appendBinaryStringInfo(buf, "?(", 2);
597  jspGetArg(v, &elem);
598  printJsonPathItem(buf, &elem, false, false);
600  break;
601  case jpiNot:
602  appendBinaryStringInfo(buf, "!(", 2);
603  jspGetArg(v, &elem);
604  printJsonPathItem(buf, &elem, false, false);
606  break;
607  case jpiIsUnknown:
609  jspGetArg(v, &elem);
610  printJsonPathItem(buf, &elem, false, false);
611  appendBinaryStringInfo(buf, ") is unknown", 12);
612  break;
613  case jpiExists:
614  appendBinaryStringInfo(buf, "exists (", 8);
615  jspGetArg(v, &elem);
616  printJsonPathItem(buf, &elem, false, false);
618  break;
619  case jpiCurrent:
620  Assert(!inKey);
622  break;
623  case jpiRoot:
624  Assert(!inKey);
626  break;
627  case jpiLast:
628  appendBinaryStringInfo(buf, "last", 4);
629  break;
630  case jpiAnyArray:
631  appendBinaryStringInfo(buf, "[*]", 3);
632  break;
633  case jpiAnyKey:
634  if (inKey)
637  break;
638  case jpiIndexArray:
640  for (i = 0; i < v->content.array.nelems; i++)
641  {
642  JsonPathItem from;
643  JsonPathItem to;
644  bool range = jspGetArraySubscript(v, &from, &to, i);
645 
646  if (i)
648 
649  printJsonPathItem(buf, &from, false, false);
650 
651  if (range)
652  {
653  appendBinaryStringInfo(buf, " to ", 4);
654  printJsonPathItem(buf, &to, false, false);
655  }
656  }
658  break;
659  case jpiAny:
660  if (inKey)
662 
663  if (v->content.anybounds.first == 0 &&
664  v->content.anybounds.last == PG_UINT32_MAX)
665  appendBinaryStringInfo(buf, "**", 2);
666  else if (v->content.anybounds.first == v->content.anybounds.last)
667  {
668  if (v->content.anybounds.first == PG_UINT32_MAX)
669  appendStringInfoString(buf, "**{last}");
670  else
671  appendStringInfo(buf, "**{%u}",
672  v->content.anybounds.first);
673  }
674  else if (v->content.anybounds.first == PG_UINT32_MAX)
675  appendStringInfo(buf, "**{last to %u}",
676  v->content.anybounds.last);
677  else if (v->content.anybounds.last == PG_UINT32_MAX)
678  appendStringInfo(buf, "**{%u to last}",
679  v->content.anybounds.first);
680  else
681  appendStringInfo(buf, "**{%u to %u}",
682  v->content.anybounds.first,
683  v->content.anybounds.last);
684  break;
685  case jpiType:
686  appendBinaryStringInfo(buf, ".type()", 7);
687  break;
688  case jpiSize:
689  appendBinaryStringInfo(buf, ".size()", 7);
690  break;
691  case jpiAbs:
692  appendBinaryStringInfo(buf, ".abs()", 6);
693  break;
694  case jpiFloor:
695  appendBinaryStringInfo(buf, ".floor()", 8);
696  break;
697  case jpiCeiling:
698  appendBinaryStringInfo(buf, ".ceiling()", 10);
699  break;
700  case jpiDouble:
701  appendBinaryStringInfo(buf, ".double()", 9);
702  break;
703  case jpiDatetime:
704  appendBinaryStringInfo(buf, ".datetime(", 10);
705  if (v->content.arg)
706  {
707  jspGetArg(v, &elem);
708  printJsonPathItem(buf, &elem, false, false);
709  }
711  break;
712  case jpiKeyValue:
713  appendBinaryStringInfo(buf, ".keyvalue()", 11);
714  break;
715  default:
716  elog(ERROR, "unrecognized jsonpath item type: %d", v->type);
717  }
718 
719  if (jspGetNext(v, &elem))
720  printJsonPathItem(buf, &elem, true, true);
721 }
722 
723 const char *
725 {
726  switch (type)
727  {
728  case jpiAnd:
729  return "&&";
730  case jpiOr:
731  return "||";
732  case jpiEqual:
733  return "==";
734  case jpiNotEqual:
735  return "!=";
736  case jpiLess:
737  return "<";
738  case jpiGreater:
739  return ">";
740  case jpiLessOrEqual:
741  return "<=";
742  case jpiGreaterOrEqual:
743  return ">=";
744  case jpiPlus:
745  case jpiAdd:
746  return "+";
747  case jpiMinus:
748  case jpiSub:
749  return "-";
750  case jpiMul:
751  return "*";
752  case jpiDiv:
753  return "/";
754  case jpiMod:
755  return "%";
756  case jpiStartsWith:
757  return "starts with";
758  case jpiLikeRegex:
759  return "like_regex";
760  case jpiType:
761  return "type";
762  case jpiSize:
763  return "size";
764  case jpiKeyValue:
765  return "keyvalue";
766  case jpiDouble:
767  return "double";
768  case jpiAbs:
769  return "abs";
770  case jpiFloor:
771  return "floor";
772  case jpiCeiling:
773  return "ceiling";
774  case jpiDatetime:
775  return "datetime";
776  default:
777  elog(ERROR, "unrecognized jsonpath item type: %d", type);
778  return NULL;
779  }
780 }
781 
782 static int
784 {
785  switch (op)
786  {
787  case jpiOr:
788  return 0;
789  case jpiAnd:
790  return 1;
791  case jpiEqual:
792  case jpiNotEqual:
793  case jpiLess:
794  case jpiGreater:
795  case jpiLessOrEqual:
796  case jpiGreaterOrEqual:
797  case jpiStartsWith:
798  return 2;
799  case jpiAdd:
800  case jpiSub:
801  return 3;
802  case jpiMul:
803  case jpiDiv:
804  case jpiMod:
805  return 4;
806  case jpiPlus:
807  case jpiMinus:
808  return 5;
809  default:
810  return 6;
811  }
812 }
813 
814 /******************* Support functions for JsonPath *************************/
815 
816 /*
817  * Support macros to read stored values
818  */
819 
820 #define read_byte(v, b, p) do { \
821  (v) = *(uint8*)((b) + (p)); \
822  (p) += 1; \
823 } while(0) \
824 
825 #define read_int32(v, b, p) do { \
826  (v) = *(uint32*)((b) + (p)); \
827  (p) += sizeof(int32); \
828 } while(0) \
829 
830 #define read_int32_n(v, b, p, n) do { \
831  (v) = (void *)((b) + (p)); \
832  (p) += sizeof(int32) * (n); \
833 } while(0) \
834 
835 /*
836  * Read root node and fill root node representation
837  */
838 void
840 {
842  jspInitByBuffer(v, js->data, 0);
843 }
844 
845 /*
846  * Read node from buffer and fill its representation
847  */
848 void
849 jspInitByBuffer(JsonPathItem *v, char *base, int32 pos)
850 {
851  v->base = base + pos;
852 
853  read_byte(v->type, base, pos);
854  pos = INTALIGN((uintptr_t) (base + pos)) - (uintptr_t) base;
855  read_int32(v->nextPos, base, pos);
856 
857  switch (v->type)
858  {
859  case jpiNull:
860  case jpiRoot:
861  case jpiCurrent:
862  case jpiAnyArray:
863  case jpiAnyKey:
864  case jpiType:
865  case jpiSize:
866  case jpiAbs:
867  case jpiFloor:
868  case jpiCeiling:
869  case jpiDouble:
870  case jpiKeyValue:
871  case jpiLast:
872  break;
873  case jpiKey:
874  case jpiString:
875  case jpiVariable:
876  read_int32(v->content.value.datalen, base, pos);
877  /* FALLTHROUGH */
878  case jpiNumeric:
879  case jpiBool:
880  v->content.value.data = base + pos;
881  break;
882  case jpiAnd:
883  case jpiOr:
884  case jpiAdd:
885  case jpiSub:
886  case jpiMul:
887  case jpiDiv:
888  case jpiMod:
889  case jpiEqual:
890  case jpiNotEqual:
891  case jpiLess:
892  case jpiGreater:
893  case jpiLessOrEqual:
894  case jpiGreaterOrEqual:
895  case jpiStartsWith:
896  read_int32(v->content.args.left, base, pos);
897  read_int32(v->content.args.right, base, pos);
898  break;
899  case jpiLikeRegex:
900  read_int32(v->content.like_regex.flags, base, pos);
901  read_int32(v->content.like_regex.expr, base, pos);
902  read_int32(v->content.like_regex.patternlen, base, pos);
903  v->content.like_regex.pattern = base + pos;
904  break;
905  case jpiNot:
906  case jpiExists:
907  case jpiIsUnknown:
908  case jpiPlus:
909  case jpiMinus:
910  case jpiFilter:
911  case jpiDatetime:
912  read_int32(v->content.arg, base, pos);
913  break;
914  case jpiIndexArray:
915  read_int32(v->content.array.nelems, base, pos);
916  read_int32_n(v->content.array.elems, base, pos,
917  v->content.array.nelems * 2);
918  break;
919  case jpiAny:
920  read_int32(v->content.anybounds.first, base, pos);
921  read_int32(v->content.anybounds.last, base, pos);
922  break;
923  default:
924  elog(ERROR, "unrecognized jsonpath item type: %d", v->type);
925  }
926 }
927 
928 void
930 {
931  Assert(v->type == jpiFilter ||
932  v->type == jpiNot ||
933  v->type == jpiIsUnknown ||
934  v->type == jpiExists ||
935  v->type == jpiPlus ||
936  v->type == jpiMinus ||
937  v->type == jpiDatetime);
938 
939  jspInitByBuffer(a, v->base, v->content.arg);
940 }
941 
942 bool
944 {
945  if (jspHasNext(v))
946  {
947  Assert(v->type == jpiString ||
948  v->type == jpiNumeric ||
949  v->type == jpiBool ||
950  v->type == jpiNull ||
951  v->type == jpiKey ||
952  v->type == jpiAny ||
953  v->type == jpiAnyArray ||
954  v->type == jpiAnyKey ||
955  v->type == jpiIndexArray ||
956  v->type == jpiFilter ||
957  v->type == jpiCurrent ||
958  v->type == jpiExists ||
959  v->type == jpiRoot ||
960  v->type == jpiVariable ||
961  v->type == jpiLast ||
962  v->type == jpiAdd ||
963  v->type == jpiSub ||
964  v->type == jpiMul ||
965  v->type == jpiDiv ||
966  v->type == jpiMod ||
967  v->type == jpiPlus ||
968  v->type == jpiMinus ||
969  v->type == jpiEqual ||
970  v->type == jpiNotEqual ||
971  v->type == jpiGreater ||
972  v->type == jpiGreaterOrEqual ||
973  v->type == jpiLess ||
974  v->type == jpiLessOrEqual ||
975  v->type == jpiAnd ||
976  v->type == jpiOr ||
977  v->type == jpiNot ||
978  v->type == jpiIsUnknown ||
979  v->type == jpiType ||
980  v->type == jpiSize ||
981  v->type == jpiAbs ||
982  v->type == jpiFloor ||
983  v->type == jpiCeiling ||
984  v->type == jpiDouble ||
985  v->type == jpiDatetime ||
986  v->type == jpiKeyValue ||
987  v->type == jpiStartsWith);
988 
989  if (a)
990  jspInitByBuffer(a, v->base, v->nextPos);
991  return true;
992  }
993 
994  return false;
995 }
996 
997 void
999 {
1000  Assert(v->type == jpiAnd ||
1001  v->type == jpiOr ||
1002  v->type == jpiEqual ||
1003  v->type == jpiNotEqual ||
1004  v->type == jpiLess ||
1005  v->type == jpiGreater ||
1006  v->type == jpiLessOrEqual ||
1007  v->type == jpiGreaterOrEqual ||
1008  v->type == jpiAdd ||
1009  v->type == jpiSub ||
1010  v->type == jpiMul ||
1011  v->type == jpiDiv ||
1012  v->type == jpiMod ||
1013  v->type == jpiStartsWith);
1014 
1015  jspInitByBuffer(a, v->base, v->content.args.left);
1016 }
1017 
1018 void
1020 {
1021  Assert(v->type == jpiAnd ||
1022  v->type == jpiOr ||
1023  v->type == jpiEqual ||
1024  v->type == jpiNotEqual ||
1025  v->type == jpiLess ||
1026  v->type == jpiGreater ||
1027  v->type == jpiLessOrEqual ||
1028  v->type == jpiGreaterOrEqual ||
1029  v->type == jpiAdd ||
1030  v->type == jpiSub ||
1031  v->type == jpiMul ||
1032  v->type == jpiDiv ||
1033  v->type == jpiMod ||
1034  v->type == jpiStartsWith);
1035 
1036  jspInitByBuffer(a, v->base, v->content.args.right);
1037 }
1038 
1039 bool
1041 {
1042  Assert(v->type == jpiBool);
1043 
1044  return (bool) *v->content.value.data;
1045 }
1046 
1047 Numeric
1049 {
1050  Assert(v->type == jpiNumeric);
1051 
1052  return (Numeric) v->content.value.data;
1053 }
1054 
1055 char *
1057 {
1058  Assert(v->type == jpiKey ||
1059  v->type == jpiString ||
1060  v->type == jpiVariable);
1061 
1062  if (len)
1063  *len = v->content.value.datalen;
1064  return v->content.value.data;
1065 }
1066 
1067 bool
1069  int i)
1070 {
1071  Assert(v->type == jpiIndexArray);
1072 
1073  jspInitByBuffer(from, v->base, v->content.array.elems[i].from);
1074 
1075  if (!v->content.array.elems[i].to)
1076  return false;
1077 
1078  jspInitByBuffer(to, v->base, v->content.array.elems[i].to);
1079 
1080  return true;
1081 }
1082 
1083 /* SQL/JSON datatype status: */
1085 {
1086  jpdsNonDateTime, /* null, bool, numeric, string, array, object */
1087  jpdsUnknownDateTime, /* unknown datetime type */
1088  jpdsDateTimeZoned, /* timetz, timestamptz */
1089  jpdsDateTimeNonZoned /* time, timestamp, date */
1091 
1092 /* Context for jspIsMutableWalker() */
1094 {
1095  List *varnames; /* list of variable names */
1096  List *varexprs; /* list of variable expressions */
1097  JsonPathDatatypeStatus current; /* status of @ item */
1098  bool lax; /* jsonpath is lax or strict */
1099  bool mutable; /* resulting mutability status */
1101 
1102 /*
1103  * Recursive walker for jspIsMutable()
1104  */
1107 {
1110 
1111  while (!cxt->mutable)
1112  {
1113  JsonPathItem arg;
1114  JsonPathDatatypeStatus leftStatus;
1115  JsonPathDatatypeStatus rightStatus;
1116 
1117  switch (jpi->type)
1118  {
1119  case jpiRoot:
1121  break;
1122 
1123  case jpiCurrent:
1125  status = cxt->current;
1126  break;
1127 
1128  case jpiFilter:
1129  {
1130  JsonPathDatatypeStatus prevStatus = cxt->current;
1131 
1132  cxt->current = status;
1133  jspGetArg(jpi, &arg);
1134  jspIsMutableWalker(&arg, cxt);
1135 
1136  cxt->current = prevStatus;
1137  break;
1138  }
1139 
1140  case jpiVariable:
1141  {
1142  int32 len;
1143  const char *name = jspGetString(jpi, &len);
1144  ListCell *lc1;
1145  ListCell *lc2;
1146 
1148 
1149  forboth(lc1, cxt->varnames, lc2, cxt->varexprs)
1150  {
1151  String *varname = lfirst_node(String, lc1);
1152  Node *varexpr = lfirst(lc2);
1153 
1154  if (strncmp(varname->sval, name, len))
1155  continue;
1156 
1157  switch (exprType(varexpr))
1158  {
1159  case DATEOID:
1160  case TIMEOID:
1161  case TIMESTAMPOID:
1163  break;
1164 
1165  case TIMETZOID:
1166  case TIMESTAMPTZOID:
1168  break;
1169 
1170  default:
1172  break;
1173  }
1174 
1175  break;
1176  }
1177  break;
1178  }
1179 
1180  case jpiEqual:
1181  case jpiNotEqual:
1182  case jpiLess:
1183  case jpiGreater:
1184  case jpiLessOrEqual:
1185  case jpiGreaterOrEqual:
1187  jspGetLeftArg(jpi, &arg);
1188  leftStatus = jspIsMutableWalker(&arg, cxt);
1189 
1190  jspGetRightArg(jpi, &arg);
1191  rightStatus = jspIsMutableWalker(&arg, cxt);
1192 
1193  /*
1194  * Comparison of datetime type with different timezone status
1195  * is mutable.
1196  */
1197  if (leftStatus != jpdsNonDateTime &&
1198  rightStatus != jpdsNonDateTime &&
1199  (leftStatus == jpdsUnknownDateTime ||
1200  rightStatus == jpdsUnknownDateTime ||
1201  leftStatus != rightStatus))
1202  cxt->mutable = true;
1203  break;
1204 
1205  case jpiNot:
1206  case jpiIsUnknown:
1207  case jpiExists:
1208  case jpiPlus:
1209  case jpiMinus:
1211  jspGetArg(jpi, &arg);
1212  jspIsMutableWalker(&arg, cxt);
1213  break;
1214 
1215  case jpiAnd:
1216  case jpiOr:
1217  case jpiAdd:
1218  case jpiSub:
1219  case jpiMul:
1220  case jpiDiv:
1221  case jpiMod:
1222  case jpiStartsWith:
1224  jspGetLeftArg(jpi, &arg);
1225  jspIsMutableWalker(&arg, cxt);
1226  jspGetRightArg(jpi, &arg);
1227  jspIsMutableWalker(&arg, cxt);
1228  break;
1229 
1230  case jpiIndexArray:
1231  for (int i = 0; i < jpi->content.array.nelems; i++)
1232  {
1233  JsonPathItem from;
1234  JsonPathItem to;
1235 
1236  if (jspGetArraySubscript(jpi, &from, &to, i))
1237  jspIsMutableWalker(&to, cxt);
1238 
1239  jspIsMutableWalker(&from, cxt);
1240  }
1241  /* FALLTHROUGH */
1242 
1243  case jpiAnyArray:
1244  if (!cxt->lax)
1246  break;
1247 
1248  case jpiAny:
1249  if (jpi->content.anybounds.first > 0)
1251  break;
1252 
1253  case jpiDatetime:
1254  if (jpi->content.arg)
1255  {
1256  char *template;
1257  int flags;
1258 
1259  jspGetArg(jpi, &arg);
1260  if (arg.type != jpiString)
1261  {
1263  break; /* there will be runtime error */
1264  }
1265 
1266  template = jspGetString(&arg, NULL);
1267  flags = datetime_format_flags(template, NULL);
1268  if (flags & DCH_ZONED)
1270  else
1272  }
1273  else
1274  {
1276  }
1277  break;
1278 
1279  case jpiLikeRegex:
1281  jspInitByBuffer(&arg, jpi->base, jpi->content.like_regex.expr);
1282  jspIsMutableWalker(&arg, cxt);
1283  break;
1284 
1285  /* literals */
1286  case jpiNull:
1287  case jpiString:
1288  case jpiNumeric:
1289  case jpiBool:
1290  /* accessors */
1291  case jpiKey:
1292  case jpiAnyKey:
1293  /* special items */
1294  case jpiSubscript:
1295  case jpiLast:
1296  /* item methods */
1297  case jpiType:
1298  case jpiSize:
1299  case jpiAbs:
1300  case jpiFloor:
1301  case jpiCeiling:
1302  case jpiDouble:
1303  case jpiKeyValue:
1305  break;
1306  }
1307 
1308  if (!jspGetNext(jpi, &next))
1309  break;
1310 
1311  jpi = &next;
1312  }
1313 
1314  return status;
1315 }
1316 
1317 /*
1318  * Check whether jsonpath expression is immutable or not.
1319  */
1320 bool
1321 jspIsMutable(JsonPath *path, List *varnames, List *varexprs)
1322 {
1324  JsonPathItem jpi;
1325 
1326  cxt.varnames = varnames;
1327  cxt.varexprs = varexprs;
1328  cxt.current = jpdsNonDateTime;
1329  cxt.lax = (path->header & JSONPATH_LAX) != 0;
1330  cxt.mutable = false;
1331 
1332  jspInit(&jpi, path);
1333  jspIsMutableWalker(&jpi, &cxt);
1334 
1335  return cxt.mutable;
1336 }
Datum numeric_out(PG_FUNCTION_ARGS)
Definition: numeric.c:735
static int32 next
Definition: blutils.c:219
#define INTALIGN(LEN)
Definition: c.h:754
#define PG_UINT32_MAX
Definition: c.h:525
signed int int32
Definition: c.h:429
int errcode(int sqlerrcode)
Definition: elog.c:693
int errmsg(const char *fmt,...)
Definition: elog.c:904
#define ERROR
Definition: elog.h:33
#define elog(elevel,...)
Definition: elog.h:218
#define ereport(elevel,...)
Definition: elog.h:143
const char * name
Definition: encode.c:561
#define PG_RETURN_BYTEA_P(x)
Definition: fmgr.h:371
#define PG_GETARG_POINTER(n)
Definition: fmgr.h:276
#define PG_RETURN_CSTRING(x)
Definition: fmgr.h:362
#define DirectFunctionCall1(func, arg1)
Definition: fmgr.h:631
#define PG_GETARG_CSTRING(n)
Definition: fmgr.h:277
#define PG_FUNCTION_ARGS
Definition: fmgr.h:193
int datetime_format_flags(const char *fmt_str, bool *have_error)
Definition: formatting.c:6716
#define DCH_ZONED
Definition: formatting.h:22
int a
Definition: isn.c:69
int i
Definition: isn.c:73
void escape_json(StringInfo buf, const char *str)
Definition: json.c:1587
bool jspIsMutable(JsonPath *path, List *varnames, List *varexprs)
Definition: jsonpath.c:1321
void jspGetLeftArg(JsonPathItem *v, JsonPathItem *a)
Definition: jsonpath.c:998
void jspGetArg(JsonPathItem *v, JsonPathItem *a)
Definition: jsonpath.c:929
Datum jsonpath_send(PG_FUNCTION_ARGS)
Definition: jsonpath.c:144
#define read_byte(v, b, p)
Definition: jsonpath.c:820
#define read_int32_n(v, b, p, n)
Definition: jsonpath.c:830
Datum jsonpath_out(PG_FUNCTION_ARGS)
Definition: jsonpath.c:131
static void alignStringInfoInt(StringInfo buf)
Definition: jsonpath.c:441
void jspInitByBuffer(JsonPathItem *v, char *base, int32 pos)
Definition: jsonpath.c:849
bool jspGetBool(JsonPathItem *v)
Definition: jsonpath.c:1040
void jspInit(JsonPathItem *v, JsonPath *js)
Definition: jsonpath.c:839
const char * jspOperationName(JsonPathItemType type)
Definition: jsonpath.c:724
Numeric jspGetNumeric(JsonPathItem *v)
Definition: jsonpath.c:1048
bool jspGetArraySubscript(JsonPathItem *v, JsonPathItem *from, JsonPathItem *to, int i)
Definition: jsonpath.c:1068
static void printJsonPathItem(StringInfo buf, JsonPathItem *v, bool inKey, bool printBracketes)
Definition: jsonpath.c:478
#define read_int32(v, b, p)
Definition: jsonpath.c:825
static int flattenJsonPathParseItem(StringInfo buf, JsonPathParseItem *item, int nestingLevel, bool insideArraySubscript)
Definition: jsonpath.c:231
bool jspGetNext(JsonPathItem *v, JsonPathItem *a)
Definition: jsonpath.c:943
static int operationPriority(JsonPathItemType op)
Definition: jsonpath.c:783
static Datum jsonPathFromCstring(char *in, int len)
Definition: jsonpath.c:170
Datum jsonpath_recv(PG_FUNCTION_ARGS)
Definition: jsonpath.c:112
Datum jsonpath_in(PG_FUNCTION_ARGS)
Definition: jsonpath.c:95
void jspGetRightArg(JsonPathItem *v, JsonPathItem *a)
Definition: jsonpath.c:1019
char * jspGetString(JsonPathItem *v, int32 *len)
Definition: jsonpath.c:1056
static int32 reserveSpaceForItemPointer(StringInfo buf)
Definition: jsonpath.c:464
static char * jsonPathToCstring(StringInfo out, JsonPath *in, int estimated_len)
Definition: jsonpath.c:205
static JsonPathDatatypeStatus jspIsMutableWalker(JsonPathItem *jpi, JsonPathMutableContext *cxt)
Definition: jsonpath.c:1106
JsonPathDatatypeStatus
Definition: jsonpath.c:1085
@ jpdsDateTimeZoned
Definition: jsonpath.c:1088
@ jpdsNonDateTime
Definition: jsonpath.c:1086
@ jpdsDateTimeNonZoned
Definition: jsonpath.c:1089
@ jpdsUnknownDateTime
Definition: jsonpath.c:1087
struct JsonPathMutableContext JsonPathMutableContext
#define JSP_REGEX_WSPACE
Definition: jsonpath.h:97
#define JSP_REGEX_MLINE
Definition: jsonpath.h:96
#define PG_RETURN_JSONPATH_P(p)
Definition: jsonpath.h:39
#define JSP_REGEX_ICASE
Definition: jsonpath.h:94
#define jspHasNext(jsp)
Definition: jsonpath.h:167
#define PG_GETARG_JSONPATH_P(x)
Definition: jsonpath.h:37
#define JSONPATH_VERSION
Definition: jsonpath.h:31
JsonPathParseResult * parsejsonpath(const char *str, int len)
#define JSP_REGEX_DOTALL
Definition: jsonpath.h:95
JsonPathItemType
Definition: jsonpath.h:47
@ jpiAdd
Definition: jsonpath.h:62
@ jpiString
Definition: jsonpath.h:49
@ jpiAbs
Definition: jsonpath.h:81
@ jpiIndexArray
Definition: jsonpath.h:71
@ jpiAny
Definition: jsonpath.h:72
@ jpiDatetime
Definition: jsonpath.h:85
@ jpiBool
Definition: jsonpath.h:51
@ jpiType
Definition: jsonpath.h:79
@ jpiFloor
Definition: jsonpath.h:82
@ jpiAnyArray
Definition: jsonpath.h:69
@ jpiExists
Definition: jsonpath.h:78
@ jpiSize
Definition: jsonpath.h:80
@ jpiSub
Definition: jsonpath.h:63
@ jpiSubscript
Definition: jsonpath.h:87
@ jpiNotEqual
Definition: jsonpath.h:57
@ jpiMul
Definition: jsonpath.h:64
@ jpiVariable
Definition: jsonpath.h:76
@ jpiNot
Definition: jsonpath.h:54
@ jpiGreaterOrEqual
Definition: jsonpath.h:61
@ jpiPlus
Definition: jsonpath.h:67
@ jpiDouble
Definition: jsonpath.h:84
@ jpiGreater
Definition: jsonpath.h:59
@ jpiAnd
Definition: jsonpath.h:52
@ jpiStartsWith
Definition: jsonpath.h:89
@ jpiOr
Definition: jsonpath.h:53
@ jpiMod
Definition: jsonpath.h:66
@ jpiLikeRegex
Definition: jsonpath.h:90
@ jpiRoot
Definition: jsonpath.h:75
@ jpiFilter
Definition: jsonpath.h:77
@ jpiNull
Definition: jsonpath.h:48
@ jpiLess
Definition: jsonpath.h:58
@ jpiCurrent
Definition: jsonpath.h:74
@ jpiEqual
Definition: jsonpath.h:56
@ jpiKey
Definition: jsonpath.h:73
@ jpiDiv
Definition: jsonpath.h:65
@ jpiLast
Definition: jsonpath.h:88
@ jpiMinus
Definition: jsonpath.h:68
@ jpiLessOrEqual
Definition: jsonpath.h:60
@ jpiCeiling
Definition: jsonpath.h:83
@ jpiIsUnknown
Definition: jsonpath.h:55
@ jpiKeyValue
Definition: jsonpath.h:86
@ jpiNumeric
Definition: jsonpath.h:50
@ jpiAnyKey
Definition: jsonpath.h:70
#define JSONPATH_LAX
Definition: jsonpath.h:32
#define JSONPATH_HDRSZ
Definition: jsonpath.h:33
#define JSP_REGEX_QUOTE
Definition: jsonpath.h:98
Assert(fmt[strlen(fmt) - 1] !='\n')
void pfree(void *pointer)
Definition: mcxt.c:1175
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:121
Oid exprType(const Node *expr)
Definition: nodeFuncs.c:41
#define NumericGetDatum(X)
Definition: numeric.h:61
void * arg
const void size_t len
#define lfirst(lc)
Definition: pg_list.h:169
#define lfirst_node(type, lc)
Definition: pg_list.h:172
#define forboth(cell1, list1, cell2, list2)
Definition: pg_list.h:446
static void static void status(const char *fmt,...) pg_attribute_printf(1
Definition: pg_regress.c:229
static char * buf
Definition: pg_test_fsync.c:67
void check_stack_depth(void)
Definition: postgres.c:3500
uintptr_t Datum
Definition: postgres.h:411
#define SET_VARSIZE(PTR, len)
Definition: postgres.h:342
#define VARSIZE(PTR)
Definition: postgres.h:316
#define DatumGetCString(X)
Definition: postgres.h:610
unsigned int pq_getmsgint(StringInfo msg, int b)
Definition: pqformat.c:417
void pq_sendtext(StringInfo buf, const char *str, int slen)
Definition: pqformat.c:174
char * pq_getmsgtext(StringInfo msg, int rawbytes, int *nbytes)
Definition: pqformat.c:548
void pq_begintypsend(StringInfo buf)
Definition: pqformat.c:328
bytea * pq_endtypsend(StringInfo buf)
Definition: pqformat.c:348
static void pq_sendint8(StringInfo buf, uint8 i)
Definition: pqformat.h:129
static struct cvec * range(struct vars *v, chr a, chr b, int cases)
Definition: regc_locale.c:412
void appendStringInfo(StringInfo str, const char *fmt,...)
Definition: stringinfo.c:91
void enlargeStringInfo(StringInfo str, int needed)
Definition: stringinfo.c:283
void appendBinaryStringInfo(StringInfo str, const char *data, int datalen)
Definition: stringinfo.c:227
void appendStringInfoSpaces(StringInfo str, int count)
Definition: stringinfo.c:206
void appendStringInfoString(StringInfo str, const char *s)
Definition: stringinfo.c:176
void appendStringInfoChar(StringInfo str, char ch)
Definition: stringinfo.c:188
void initStringInfo(StringInfo str)
Definition: stringinfo.c:59
StringInfoData * StringInfo
Definition: stringinfo.h:44
#define appendStringInfoCharMacro(str, ch)
Definition: stringinfo.h:128
struct JsonPathItem::@136::@140 value
char * base
Definition: jsonpath.h:119
int32 nextPos
Definition: jsonpath.h:113
struct JsonPathItem::@136::@141 like_regex
int32 arg
Definition: jsonpath.h:131
JsonPathItemType type
Definition: jsonpath.h:110
struct JsonPathItem::@136::@137 args
struct JsonPathItem::@136::@139 anybounds
struct JsonPathItem::@136::@138 array
union JsonPathItem::@136 content
JsonPathDatatypeStatus current
Definition: jsonpath.c:1097
struct JsonPathParseItem::@143::@144 args
JsonPathParseItem * arg
Definition: jsonpath.h:206
struct JsonPathParseItem::@143::@146 anybounds
JsonPathParseItem * next
Definition: jsonpath.h:193
struct JsonPathParseItem::@143::@148 string
Numeric numeric
Definition: jsonpath.h:235
struct JsonPathParseItem::@143::@145 array
struct JsonPathParseItem::@143::@147 like_regex
JsonPathItemType type
Definition: jsonpath.h:192
union JsonPathParseItem::@143 value
JsonPathParseItem * expr
Definition: jsonpath.h:247
char data[FLEXIBLE_ARRAY_MEMBER]
Definition: jsonpath.h:28
uint32 header
Definition: jsonpath.h:27
Definition: pg_list.h:51
Definition: nodes.h:574
Definition: value.h:58
char * sval
Definition: value.h:60