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, 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 "utils/builtins.h"
71 #include "utils/json.h"
72 #include "utils/jsonpath.h"
73 
74 
75 static Datum jsonPathFromCstring(char *in, int len);
76 static char *jsonPathToCstring(StringInfo out, JsonPath *in,
77  int estimated_len);
79  int nestingLevel, bool insideArraySubscript);
80 static void alignStringInfoInt(StringInfo buf);
82 static void printJsonPathItem(StringInfo buf, JsonPathItem *v, bool inKey,
83  bool printBracketes);
84 static int operationPriority(JsonPathItemType op);
85 
86 
87 /**************************** INPUT/OUTPUT ********************************/
88 
89 /*
90  * jsonpath type input function
91  */
92 Datum
94 {
95  char *in = PG_GETARG_CSTRING(0);
96  int len = strlen(in);
97 
98  return jsonPathFromCstring(in, len);
99 }
100 
101 /*
102  * jsonpath type recv function
103  *
104  * The type is sent as text in binary mode, so this is almost the same
105  * as the input function, but it's prefixed with a version number so we
106  * can change the binary format sent in future if necessary. For now,
107  * only version 1 is supported.
108  */
109 Datum
111 {
113  int version = pq_getmsgint(buf, 1);
114  char *str;
115  int nbytes;
116 
117  if (version == JSONPATH_VERSION)
118  str = pq_getmsgtext(buf, buf->len - buf->cursor, &nbytes);
119  else
120  elog(ERROR, "unsupported jsonpath version number: %d", version);
121 
122  return jsonPathFromCstring(str, nbytes);
123 }
124 
125 /*
126  * jsonpath type output function
127  */
128 Datum
130 {
132 
134 }
135 
136 /*
137  * jsonpath type send function
138  *
139  * Just send jsonpath as a version number, then a string of text
140  */
141 Datum
143 {
146  StringInfoData jtext;
147  int version = JSONPATH_VERSION;
148 
149  initStringInfo(&jtext);
150  (void) jsonPathToCstring(&jtext, in, VARSIZE(in));
151 
152  pq_begintypsend(&buf);
153  pq_sendint8(&buf, version);
154  pq_sendtext(&buf, jtext.data, jtext.len);
155  pfree(jtext.data);
156 
158 }
159 
160 /*
161  * Converts C-string to a jsonpath value.
162  *
163  * Uses jsonpath parser to turn string into an AST, then
164  * flattenJsonPathParseItem() does second pass turning AST into binary
165  * representation of jsonpath.
166  */
167 static Datum
168 jsonPathFromCstring(char *in, int len)
169 {
170  JsonPathParseResult *jsonpath = parsejsonpath(in, len);
171  JsonPath *res;
173 
174  initStringInfo(&buf);
175  enlargeStringInfo(&buf, 4 * len /* estimation */ );
176 
178 
179  if (!jsonpath)
180  ereport(ERROR,
181  (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
182  errmsg("invalid input syntax for type %s: \"%s\"", "jsonpath",
183  in)));
184 
185  flattenJsonPathParseItem(&buf, jsonpath->expr, 0, false);
186 
187  res = (JsonPath *) buf.data;
188  SET_VARSIZE(res, buf.len);
189  res->header = JSONPATH_VERSION;
190  if (jsonpath->lax)
191  res->header |= JSONPATH_LAX;
192 
194 }
195 
196 /*
197  * Converts jsonpath value to a C-string.
198  *
199  * If 'out' argument is non-null, the resulting C-string is stored inside the
200  * StringBuffer. The resulting string is always returned.
201  */
202 static char *
203 jsonPathToCstring(StringInfo out, JsonPath *in, int estimated_len)
204 {
206  JsonPathItem v;
207 
208  if (!out)
209  {
210  out = &buf;
211  initStringInfo(out);
212  }
213  enlargeStringInfo(out, estimated_len);
214 
215  if (!(in->header & JSONPATH_LAX))
216  appendBinaryStringInfo(out, "strict ", 7);
217 
218  jspInit(&v, in);
219  printJsonPathItem(out, &v, false, true);
220 
221  return out->data;
222 }
223 
224 /*
225  * Recursive function converting given jsonpath parse item and all its
226  * children into a binary representation.
227  */
228 static int
230  int nestingLevel, bool insideArraySubscript)
231 {
232  /* position from beginning of jsonpath data */
233  int32 pos = buf->len - JSONPATH_HDRSZ;
234  int32 chld;
235  int32 next;
236  int argNestingLevel = 0;
237 
240 
241  appendStringInfoChar(buf, (char) (item->type));
242 
243  /*
244  * We align buffer to int32 because a series of int32 values often goes
245  * after the header, and we want to read them directly by dereferencing
246  * int32 pointer (see jspInitByBuffer()).
247  */
248  alignStringInfoInt(buf);
249 
250  /*
251  * Reserve space for next item pointer. Actual value will be recorded
252  * later, after next and children items processing.
253  */
254  next = reserveSpaceForItemPointer(buf);
255 
256  switch (item->type)
257  {
258  case jpiString:
259  case jpiVariable:
260  case jpiKey:
261  appendBinaryStringInfo(buf, (char *) &item->value.string.len,
262  sizeof(item->value.string.len));
263  appendBinaryStringInfo(buf, item->value.string.val,
264  item->value.string.len);
265  appendStringInfoChar(buf, '\0');
266  break;
267  case jpiNumeric:
268  appendBinaryStringInfo(buf, (char *) item->value.numeric,
269  VARSIZE(item->value.numeric));
270  break;
271  case jpiBool:
272  appendBinaryStringInfo(buf, (char *) &item->value.boolean,
273  sizeof(item->value.boolean));
274  break;
275  case jpiAnd:
276  case jpiOr:
277  case jpiEqual:
278  case jpiNotEqual:
279  case jpiLess:
280  case jpiGreater:
281  case jpiLessOrEqual:
282  case jpiGreaterOrEqual:
283  case jpiAdd:
284  case jpiSub:
285  case jpiMul:
286  case jpiDiv:
287  case jpiMod:
288  case jpiStartsWith:
289  {
290  /*
291  * First, reserve place for left/right arg's positions, then
292  * record both args and sets actual position in reserved
293  * places.
294  */
295  int32 left = reserveSpaceForItemPointer(buf);
296  int32 right = reserveSpaceForItemPointer(buf);
297 
298  chld = !item->value.args.left ? pos :
299  flattenJsonPathParseItem(buf, item->value.args.left,
300  nestingLevel + argNestingLevel,
301  insideArraySubscript);
302  *(int32 *) (buf->data + left) = chld - pos;
303 
304  chld = !item->value.args.right ? pos :
305  flattenJsonPathParseItem(buf, item->value.args.right,
306  nestingLevel + argNestingLevel,
307  insideArraySubscript);
308  *(int32 *) (buf->data + right) = chld - pos;
309  }
310  break;
311  case jpiLikeRegex:
312  {
313  int32 offs;
314 
316  (char *) &item->value.like_regex.flags,
317  sizeof(item->value.like_regex.flags));
318  offs = reserveSpaceForItemPointer(buf);
320  (char *) &item->value.like_regex.patternlen,
321  sizeof(item->value.like_regex.patternlen));
322  appendBinaryStringInfo(buf, item->value.like_regex.pattern,
323  item->value.like_regex.patternlen);
324  appendStringInfoChar(buf, '\0');
325 
326  chld = flattenJsonPathParseItem(buf, item->value.like_regex.expr,
327  nestingLevel,
328  insideArraySubscript);
329  *(int32 *) (buf->data + offs) = chld - pos;
330  }
331  break;
332  case jpiFilter:
333  argNestingLevel++;
334  /* FALLTHROUGH */
335  case jpiIsUnknown:
336  case jpiNot:
337  case jpiPlus:
338  case jpiMinus:
339  case jpiExists:
340  case jpiDatetime:
341  {
343 
344  chld = !item->value.arg ? pos :
346  nestingLevel + argNestingLevel,
347  insideArraySubscript);
348  *(int32 *) (buf->data + arg) = chld - pos;
349  }
350  break;
351  case jpiNull:
352  break;
353  case jpiRoot:
354  break;
355  case jpiAnyArray:
356  case jpiAnyKey:
357  break;
358  case jpiCurrent:
359  if (nestingLevel <= 0)
360  ereport(ERROR,
361  (errcode(ERRCODE_SYNTAX_ERROR),
362  errmsg("@ is not allowed in root expressions")));
363  break;
364  case jpiLast:
365  if (!insideArraySubscript)
366  ereport(ERROR,
367  (errcode(ERRCODE_SYNTAX_ERROR),
368  errmsg("LAST is allowed only in array subscripts")));
369  break;
370  case jpiIndexArray:
371  {
372  int32 nelems = item->value.array.nelems;
373  int offset;
374  int i;
375 
376  appendBinaryStringInfo(buf, (char *) &nelems, sizeof(nelems));
377 
378  offset = buf->len;
379 
380  appendStringInfoSpaces(buf, sizeof(int32) * 2 * nelems);
381 
382  for (i = 0; i < nelems; i++)
383  {
384  int32 *ppos;
385  int32 topos;
386  int32 frompos =
388  item->value.array.elems[i].from,
389  nestingLevel, true) - pos;
390 
391  if (item->value.array.elems[i].to)
392  topos = flattenJsonPathParseItem(buf,
393  item->value.array.elems[i].to,
394  nestingLevel, true) - pos;
395  else
396  topos = 0;
397 
398  ppos = (int32 *) &buf->data[offset + i * 2 * sizeof(int32)];
399 
400  ppos[0] = frompos;
401  ppos[1] = topos;
402  }
403  }
404  break;
405  case jpiAny:
407  (char *) &item->value.anybounds.first,
408  sizeof(item->value.anybounds.first));
410  (char *) &item->value.anybounds.last,
411  sizeof(item->value.anybounds.last));
412  break;
413  case jpiType:
414  case jpiSize:
415  case jpiAbs:
416  case jpiFloor:
417  case jpiCeiling:
418  case jpiDouble:
419  case jpiKeyValue:
420  break;
421  default:
422  elog(ERROR, "unrecognized jsonpath item type: %d", item->type);
423  }
424 
425  if (item->next)
426  {
427  chld = flattenJsonPathParseItem(buf, item->next, nestingLevel,
428  insideArraySubscript) - pos;
429  *(int32 *) (buf->data + next) = chld;
430  }
431 
432  return pos;
433 }
434 
435 /*
436  * Align StringInfo to int by adding zero padding bytes
437  */
438 static void
440 {
441  switch (INTALIGN(buf->len) - buf->len)
442  {
443  case 3:
445  /* FALLTHROUGH */
446  case 2:
448  /* FALLTHROUGH */
449  case 1:
451  /* FALLTHROUGH */
452  default:
453  break;
454  }
455 }
456 
457 /*
458  * Reserve space for int32 JsonPathItem pointer. Now zero pointer is written,
459  * actual value will be recorded at '(int32 *) &buf->data[pos]' later.
460  */
461 static int32
463 {
464  int32 pos = buf->len;
465  int32 ptr = 0;
466 
467  appendBinaryStringInfo(buf, (char *) &ptr, sizeof(ptr));
468 
469  return pos;
470 }
471 
472 /*
473  * Prints text representation of given jsonpath item and all its children.
474  */
475 static void
477  bool printBracketes)
478 {
479  JsonPathItem elem;
480  int i;
481 
484 
485  switch (v->type)
486  {
487  case jpiNull:
488  appendStringInfoString(buf, "null");
489  break;
490  case jpiKey:
491  if (inKey)
492  appendStringInfoChar(buf, '.');
493  escape_json(buf, jspGetString(v, NULL));
494  break;
495  case jpiString:
496  escape_json(buf, jspGetString(v, NULL));
497  break;
498  case jpiVariable:
499  appendStringInfoChar(buf, '$');
500  escape_json(buf, jspGetString(v, NULL));
501  break;
502  case jpiNumeric:
506  break;
507  case jpiBool:
508  if (jspGetBool(v))
509  appendBinaryStringInfo(buf, "true", 4);
510  else
511  appendBinaryStringInfo(buf, "false", 5);
512  break;
513  case jpiAnd:
514  case jpiOr:
515  case jpiEqual:
516  case jpiNotEqual:
517  case jpiLess:
518  case jpiGreater:
519  case jpiLessOrEqual:
520  case jpiGreaterOrEqual:
521  case jpiAdd:
522  case jpiSub:
523  case jpiMul:
524  case jpiDiv:
525  case jpiMod:
526  case jpiStartsWith:
527  if (printBracketes)
528  appendStringInfoChar(buf, '(');
529  jspGetLeftArg(v, &elem);
530  printJsonPathItem(buf, &elem, false,
531  operationPriority(elem.type) <=
532  operationPriority(v->type));
533  appendStringInfoChar(buf, ' ');
535  appendStringInfoChar(buf, ' ');
536  jspGetRightArg(v, &elem);
537  printJsonPathItem(buf, &elem, false,
538  operationPriority(elem.type) <=
539  operationPriority(v->type));
540  if (printBracketes)
541  appendStringInfoChar(buf, ')');
542  break;
543  case jpiLikeRegex:
544  if (printBracketes)
545  appendStringInfoChar(buf, '(');
546 
547  jspInitByBuffer(&elem, v->base, v->content.like_regex.expr);
548  printJsonPathItem(buf, &elem, false,
549  operationPriority(elem.type) <=
550  operationPriority(v->type));
551 
552  appendBinaryStringInfo(buf, " like_regex ", 12);
553 
554  escape_json(buf, v->content.like_regex.pattern);
555 
556  if (v->content.like_regex.flags)
557  {
558  appendBinaryStringInfo(buf, " flag \"", 7);
559 
560  if (v->content.like_regex.flags & JSP_REGEX_ICASE)
561  appendStringInfoChar(buf, 'i');
562  if (v->content.like_regex.flags & JSP_REGEX_DOTALL)
563  appendStringInfoChar(buf, 's');
564  if (v->content.like_regex.flags & JSP_REGEX_MLINE)
565  appendStringInfoChar(buf, 'm');
566  if (v->content.like_regex.flags & JSP_REGEX_WSPACE)
567  appendStringInfoChar(buf, 'x');
568  if (v->content.like_regex.flags & JSP_REGEX_QUOTE)
569  appendStringInfoChar(buf, 'q');
570 
571  appendStringInfoChar(buf, '"');
572  }
573 
574  if (printBracketes)
575  appendStringInfoChar(buf, ')');
576  break;
577  case jpiPlus:
578  case jpiMinus:
579  if (printBracketes)
580  appendStringInfoChar(buf, '(');
581  appendStringInfoChar(buf, v->type == jpiPlus ? '+' : '-');
582  jspGetArg(v, &elem);
583  printJsonPathItem(buf, &elem, false,
584  operationPriority(elem.type) <=
585  operationPriority(v->type));
586  if (printBracketes)
587  appendStringInfoChar(buf, ')');
588  break;
589  case jpiFilter:
590  appendBinaryStringInfo(buf, "?(", 2);
591  jspGetArg(v, &elem);
592  printJsonPathItem(buf, &elem, false, false);
593  appendStringInfoChar(buf, ')');
594  break;
595  case jpiNot:
596  appendBinaryStringInfo(buf, "!(", 2);
597  jspGetArg(v, &elem);
598  printJsonPathItem(buf, &elem, false, false);
599  appendStringInfoChar(buf, ')');
600  break;
601  case jpiIsUnknown:
602  appendStringInfoChar(buf, '(');
603  jspGetArg(v, &elem);
604  printJsonPathItem(buf, &elem, false, false);
605  appendBinaryStringInfo(buf, ") is unknown", 12);
606  break;
607  case jpiExists:
608  appendBinaryStringInfo(buf, "exists (", 8);
609  jspGetArg(v, &elem);
610  printJsonPathItem(buf, &elem, false, false);
611  appendStringInfoChar(buf, ')');
612  break;
613  case jpiCurrent:
614  Assert(!inKey);
615  appendStringInfoChar(buf, '@');
616  break;
617  case jpiRoot:
618  Assert(!inKey);
619  appendStringInfoChar(buf, '$');
620  break;
621  case jpiLast:
622  appendBinaryStringInfo(buf, "last", 4);
623  break;
624  case jpiAnyArray:
625  appendBinaryStringInfo(buf, "[*]", 3);
626  break;
627  case jpiAnyKey:
628  if (inKey)
629  appendStringInfoChar(buf, '.');
630  appendStringInfoChar(buf, '*');
631  break;
632  case jpiIndexArray:
633  appendStringInfoChar(buf, '[');
634  for (i = 0; i < v->content.array.nelems; i++)
635  {
636  JsonPathItem from;
637  JsonPathItem to;
638  bool range = jspGetArraySubscript(v, &from, &to, i);
639 
640  if (i)
641  appendStringInfoChar(buf, ',');
642 
643  printJsonPathItem(buf, &from, false, false);
644 
645  if (range)
646  {
647  appendBinaryStringInfo(buf, " to ", 4);
648  printJsonPathItem(buf, &to, false, false);
649  }
650  }
651  appendStringInfoChar(buf, ']');
652  break;
653  case jpiAny:
654  if (inKey)
655  appendStringInfoChar(buf, '.');
656 
657  if (v->content.anybounds.first == 0 &&
658  v->content.anybounds.last == PG_UINT32_MAX)
659  appendBinaryStringInfo(buf, "**", 2);
660  else if (v->content.anybounds.first == v->content.anybounds.last)
661  {
662  if (v->content.anybounds.first == PG_UINT32_MAX)
663  appendStringInfo(buf, "**{last}");
664  else
665  appendStringInfo(buf, "**{%u}",
666  v->content.anybounds.first);
667  }
668  else if (v->content.anybounds.first == PG_UINT32_MAX)
669  appendStringInfo(buf, "**{last to %u}",
670  v->content.anybounds.last);
671  else if (v->content.anybounds.last == PG_UINT32_MAX)
672  appendStringInfo(buf, "**{%u to last}",
673  v->content.anybounds.first);
674  else
675  appendStringInfo(buf, "**{%u to %u}",
676  v->content.anybounds.first,
677  v->content.anybounds.last);
678  break;
679  case jpiType:
680  appendBinaryStringInfo(buf, ".type()", 7);
681  break;
682  case jpiSize:
683  appendBinaryStringInfo(buf, ".size()", 7);
684  break;
685  case jpiAbs:
686  appendBinaryStringInfo(buf, ".abs()", 6);
687  break;
688  case jpiFloor:
689  appendBinaryStringInfo(buf, ".floor()", 8);
690  break;
691  case jpiCeiling:
692  appendBinaryStringInfo(buf, ".ceiling()", 10);
693  break;
694  case jpiDouble:
695  appendBinaryStringInfo(buf, ".double()", 9);
696  break;
697  case jpiDatetime:
698  appendBinaryStringInfo(buf, ".datetime(", 10);
699  if (v->content.arg)
700  {
701  jspGetArg(v, &elem);
702  printJsonPathItem(buf, &elem, false, false);
703  }
704  appendStringInfoChar(buf, ')');
705  break;
706  case jpiKeyValue:
707  appendBinaryStringInfo(buf, ".keyvalue()", 11);
708  break;
709  default:
710  elog(ERROR, "unrecognized jsonpath item type: %d", v->type);
711  }
712 
713  if (jspGetNext(v, &elem))
714  printJsonPathItem(buf, &elem, true, true);
715 }
716 
717 const char *
719 {
720  switch (type)
721  {
722  case jpiAnd:
723  return "&&";
724  case jpiOr:
725  return "||";
726  case jpiEqual:
727  return "==";
728  case jpiNotEqual:
729  return "!=";
730  case jpiLess:
731  return "<";
732  case jpiGreater:
733  return ">";
734  case jpiLessOrEqual:
735  return "<=";
736  case jpiGreaterOrEqual:
737  return ">=";
738  case jpiPlus:
739  case jpiAdd:
740  return "+";
741  case jpiMinus:
742  case jpiSub:
743  return "-";
744  case jpiMul:
745  return "*";
746  case jpiDiv:
747  return "/";
748  case jpiMod:
749  return "%";
750  case jpiStartsWith:
751  return "starts with";
752  case jpiLikeRegex:
753  return "like_regex";
754  case jpiType:
755  return "type";
756  case jpiSize:
757  return "size";
758  case jpiKeyValue:
759  return "keyvalue";
760  case jpiDouble:
761  return "double";
762  case jpiAbs:
763  return "abs";
764  case jpiFloor:
765  return "floor";
766  case jpiCeiling:
767  return "ceiling";
768  case jpiDatetime:
769  return "datetime";
770  default:
771  elog(ERROR, "unrecognized jsonpath item type: %d", type);
772  return NULL;
773  }
774 }
775 
776 static int
778 {
779  switch (op)
780  {
781  case jpiOr:
782  return 0;
783  case jpiAnd:
784  return 1;
785  case jpiEqual:
786  case jpiNotEqual:
787  case jpiLess:
788  case jpiGreater:
789  case jpiLessOrEqual:
790  case jpiGreaterOrEqual:
791  case jpiStartsWith:
792  return 2;
793  case jpiAdd:
794  case jpiSub:
795  return 3;
796  case jpiMul:
797  case jpiDiv:
798  case jpiMod:
799  return 4;
800  case jpiPlus:
801  case jpiMinus:
802  return 5;
803  default:
804  return 6;
805  }
806 }
807 
808 /******************* Support functions for JsonPath *************************/
809 
810 /*
811  * Support macros to read stored values
812  */
813 
814 #define read_byte(v, b, p) do { \
815  (v) = *(uint8*)((b) + (p)); \
816  (p) += 1; \
817 } while(0) \
818 
819 #define read_int32(v, b, p) do { \
820  (v) = *(uint32*)((b) + (p)); \
821  (p) += sizeof(int32); \
822 } while(0) \
823 
824 #define read_int32_n(v, b, p, n) do { \
825  (v) = (void *)((b) + (p)); \
826  (p) += sizeof(int32) * (n); \
827 } while(0) \
828 
829 /*
830  * Read root node and fill root node representation
831  */
832 void
834 {
836  jspInitByBuffer(v, js->data, 0);
837 }
838 
839 /*
840  * Read node from buffer and fill its representation
841  */
842 void
843 jspInitByBuffer(JsonPathItem *v, char *base, int32 pos)
844 {
845  v->base = base + pos;
846 
847  read_byte(v->type, base, pos);
848  pos = INTALIGN((uintptr_t) (base + pos)) - (uintptr_t) base;
849  read_int32(v->nextPos, base, pos);
850 
851  switch (v->type)
852  {
853  case jpiNull:
854  case jpiRoot:
855  case jpiCurrent:
856  case jpiAnyArray:
857  case jpiAnyKey:
858  case jpiType:
859  case jpiSize:
860  case jpiAbs:
861  case jpiFloor:
862  case jpiCeiling:
863  case jpiDouble:
864  case jpiKeyValue:
865  case jpiLast:
866  break;
867  case jpiKey:
868  case jpiString:
869  case jpiVariable:
870  read_int32(v->content.value.datalen, base, pos);
871  /* FALLTHROUGH */
872  case jpiNumeric:
873  case jpiBool:
874  v->content.value.data = base + pos;
875  break;
876  case jpiAnd:
877  case jpiOr:
878  case jpiAdd:
879  case jpiSub:
880  case jpiMul:
881  case jpiDiv:
882  case jpiMod:
883  case jpiEqual:
884  case jpiNotEqual:
885  case jpiLess:
886  case jpiGreater:
887  case jpiLessOrEqual:
888  case jpiGreaterOrEqual:
889  case jpiStartsWith:
890  read_int32(v->content.args.left, base, pos);
891  read_int32(v->content.args.right, base, pos);
892  break;
893  case jpiLikeRegex:
894  read_int32(v->content.like_regex.flags, base, pos);
895  read_int32(v->content.like_regex.expr, base, pos);
896  read_int32(v->content.like_regex.patternlen, base, pos);
897  v->content.like_regex.pattern = base + pos;
898  break;
899  case jpiNot:
900  case jpiExists:
901  case jpiIsUnknown:
902  case jpiPlus:
903  case jpiMinus:
904  case jpiFilter:
905  case jpiDatetime:
906  read_int32(v->content.arg, base, pos);
907  break;
908  case jpiIndexArray:
909  read_int32(v->content.array.nelems, base, pos);
910  read_int32_n(v->content.array.elems, base, pos,
911  v->content.array.nelems * 2);
912  break;
913  case jpiAny:
914  read_int32(v->content.anybounds.first, base, pos);
915  read_int32(v->content.anybounds.last, base, pos);
916  break;
917  default:
918  elog(ERROR, "unrecognized jsonpath item type: %d", v->type);
919  }
920 }
921 
922 void
924 {
925  Assert(v->type == jpiFilter ||
926  v->type == jpiNot ||
927  v->type == jpiIsUnknown ||
928  v->type == jpiExists ||
929  v->type == jpiPlus ||
930  v->type == jpiMinus ||
931  v->type == jpiDatetime);
932 
933  jspInitByBuffer(a, v->base, v->content.arg);
934 }
935 
936 bool
938 {
939  if (jspHasNext(v))
940  {
941  Assert(v->type == jpiString ||
942  v->type == jpiNumeric ||
943  v->type == jpiBool ||
944  v->type == jpiNull ||
945  v->type == jpiKey ||
946  v->type == jpiAny ||
947  v->type == jpiAnyArray ||
948  v->type == jpiAnyKey ||
949  v->type == jpiIndexArray ||
950  v->type == jpiFilter ||
951  v->type == jpiCurrent ||
952  v->type == jpiExists ||
953  v->type == jpiRoot ||
954  v->type == jpiVariable ||
955  v->type == jpiLast ||
956  v->type == jpiAdd ||
957  v->type == jpiSub ||
958  v->type == jpiMul ||
959  v->type == jpiDiv ||
960  v->type == jpiMod ||
961  v->type == jpiPlus ||
962  v->type == jpiMinus ||
963  v->type == jpiEqual ||
964  v->type == jpiNotEqual ||
965  v->type == jpiGreater ||
966  v->type == jpiGreaterOrEqual ||
967  v->type == jpiLess ||
968  v->type == jpiLessOrEqual ||
969  v->type == jpiAnd ||
970  v->type == jpiOr ||
971  v->type == jpiNot ||
972  v->type == jpiIsUnknown ||
973  v->type == jpiType ||
974  v->type == jpiSize ||
975  v->type == jpiAbs ||
976  v->type == jpiFloor ||
977  v->type == jpiCeiling ||
978  v->type == jpiDouble ||
979  v->type == jpiDatetime ||
980  v->type == jpiKeyValue ||
981  v->type == jpiStartsWith);
982 
983  if (a)
984  jspInitByBuffer(a, v->base, v->nextPos);
985  return true;
986  }
987 
988  return false;
989 }
990 
991 void
993 {
994  Assert(v->type == jpiAnd ||
995  v->type == jpiOr ||
996  v->type == jpiEqual ||
997  v->type == jpiNotEqual ||
998  v->type == jpiLess ||
999  v->type == jpiGreater ||
1000  v->type == jpiLessOrEqual ||
1001  v->type == jpiGreaterOrEqual ||
1002  v->type == jpiAdd ||
1003  v->type == jpiSub ||
1004  v->type == jpiMul ||
1005  v->type == jpiDiv ||
1006  v->type == jpiMod ||
1007  v->type == jpiStartsWith);
1008 
1009  jspInitByBuffer(a, v->base, v->content.args.left);
1010 }
1011 
1012 void
1014 {
1015  Assert(v->type == jpiAnd ||
1016  v->type == jpiOr ||
1017  v->type == jpiEqual ||
1018  v->type == jpiNotEqual ||
1019  v->type == jpiLess ||
1020  v->type == jpiGreater ||
1021  v->type == jpiLessOrEqual ||
1022  v->type == jpiGreaterOrEqual ||
1023  v->type == jpiAdd ||
1024  v->type == jpiSub ||
1025  v->type == jpiMul ||
1026  v->type == jpiDiv ||
1027  v->type == jpiMod ||
1028  v->type == jpiStartsWith);
1029 
1030  jspInitByBuffer(a, v->base, v->content.args.right);
1031 }
1032 
1033 bool
1035 {
1036  Assert(v->type == jpiBool);
1037 
1038  return (bool) *v->content.value.data;
1039 }
1040 
1041 Numeric
1043 {
1044  Assert(v->type == jpiNumeric);
1045 
1046  return (Numeric) v->content.value.data;
1047 }
1048 
1049 char *
1051 {
1052  Assert(v->type == jpiKey ||
1053  v->type == jpiString ||
1054  v->type == jpiVariable);
1055 
1056  if (len)
1057  *len = v->content.value.datalen;
1058  return v->content.value.data;
1059 }
1060 
1061 bool
1063  int i)
1064 {
1065  Assert(v->type == jpiIndexArray);
1066 
1067  jspInitByBuffer(from, v->base, v->content.array.elems[i].from);
1068 
1069  if (!v->content.array.elems[i].to)
1070  return false;
1071 
1072  jspInitByBuffer(to, v->base, v->content.array.elems[i].to);
1073 
1074  return true;
1075 }
#define JSP_REGEX_DOTALL
Definition: jsonpath.h:92
struct JsonPathItem::@130::@134 value
struct JsonPathParseItem::@137::@141 like_regex
Datum jsonpath_recv(PG_FUNCTION_ARGS)
Definition: jsonpath.c:110
static int32 next
Definition: blutils.c:215
#define read_int32(v, b, p)
Definition: jsonpath.c:819
void escape_json(StringInfo buf, const char *str)
Definition: json.c:2483
bool jspGetNext(JsonPathItem *v, JsonPathItem *a)
Definition: jsonpath.c:937
Definition: jsonpath.h:50
struct JsonPathItem::@130::@133 anybounds
#define VARSIZE(PTR)
Definition: postgres.h:303
void pq_begintypsend(StringInfo buf)
Definition: pqformat.c:328
JsonPathParseResult * parsejsonpath(const char *str, int len)
char * base
Definition: jsonpath.h:116
#define NumericGetDatum(X)
Definition: numeric.h:51
StringInfoData * StringInfo
Definition: stringinfo.h:43
Datum numeric_out(PG_FUNCTION_ARGS)
Definition: numeric.c:655
JsonPathParseItem * arg
Definition: jsonpath.h:202
#define jspHasNext(jsp)
Definition: jsonpath.h:164
void jspInitByBuffer(JsonPathItem *v, char *base, int32 pos)
Definition: jsonpath.c:843
int errcode(int sqlerrcode)
Definition: elog.c:570
#define JSP_REGEX_ICASE
Definition: jsonpath.h:91
void pq_sendtext(StringInfo buf, const char *str, int slen)
Definition: pqformat.c:174
struct JsonPathParseItem::@137::@139 array
static char * jsonPathToCstring(StringInfo out, JsonPath *in, int estimated_len)
Definition: jsonpath.c:203
#define PG_GETARG_POINTER(n)
Definition: fmgr.h:271
#define INTALIGN(LEN)
Definition: c.h:682
#define DirectFunctionCall1(func, arg1)
Definition: fmgr.h:617
JsonPathParseItem * next
Definition: jsonpath.h:189
#define PG_RETURN_BYTEA_P(x)
Definition: fmgr.h:360
#define PG_UINT32_MAX
Definition: c.h:442
bytea * pq_endtypsend(StringInfo buf)
Definition: pqformat.c:348
#define JSONPATH_LAX
Definition: jsonpath.h:29
int32 arg
Definition: jsonpath.h:128
signed int int32
Definition: c.h:346
JsonPathParseItem * expr
Definition: jsonpath.h:243
JsonPathItemType
Definition: jsonpath.h:43
#define appendStringInfoCharMacro(str, ch)
Definition: stringinfo.h:127
#define PG_RETURN_JSONPATH_P(p)
Definition: jsonpath.h:36
void pfree(void *pointer)
Definition: mcxt.c:1056
void appendStringInfo(StringInfo str, const char *fmt,...)
Definition: stringinfo.c:78
#define ERROR
Definition: elog.h:43
#define JSP_REGEX_MLINE
Definition: jsonpath.h:93
#define DatumGetCString(X)
Definition: postgres.h:566
Datum jsonpath_send(PG_FUNCTION_ARGS)
Definition: jsonpath.c:142
#define JSP_REGEX_WSPACE
Definition: jsonpath.h:94
static void alignStringInfoInt(StringInfo buf)
Definition: jsonpath.c:439
void appendStringInfoString(StringInfo str, const char *s)
Definition: stringinfo.c:163
static char * buf
Definition: pg_test_fsync.c:68
#define JSONPATH_VERSION
Definition: jsonpath.h:28
void check_stack_depth(void)
Definition: postgres.c:3262
static Datum jsonPathFromCstring(char *in, int len)
Definition: jsonpath.c:168
union JsonPathItem::@130 content
#define JSP_REGEX_QUOTE
Definition: jsonpath.h:95
void enlargeStringInfo(StringInfo str, int needed)
Definition: stringinfo.c:270
static struct cvec * range(struct vars *v, chr a, chr b, int cases)
Definition: regc_locale.c:416
#define ereport(elevel, rest)
Definition: elog.h:141
char * pq_getmsgtext(StringInfo msg, int rawbytes, int *nbytes)
Definition: pqformat.c:548
#define PG_GETARG_JSONPATH_P(x)
Definition: jsonpath.h:34
void appendStringInfoChar(StringInfo str, char ch)
Definition: stringinfo.c:175
void initStringInfo(StringInfo str)
Definition: stringinfo.c:46
const char * jspOperationName(JsonPathItemType type)
Definition: jsonpath.c:718
struct JsonPathParseItem::@137::@142 string
#define read_int32_n(v, b, p, n)
Definition: jsonpath.c:824
void jspGetRightArg(JsonPathItem *v, JsonPathItem *a)
Definition: jsonpath.c:1013
uintptr_t Datum
Definition: postgres.h:367
Datum jsonpath_in(PG_FUNCTION_ARGS)
Definition: jsonpath.c:93
void appendStringInfoSpaces(StringInfo str, int count)
Definition: stringinfo.c:193
bool jspGetBool(JsonPathItem *v)
Definition: jsonpath.c:1034
bool jspGetArraySubscript(JsonPathItem *v, JsonPathItem *from, JsonPathItem *to, int i)
Definition: jsonpath.c:1062
void jspGetLeftArg(JsonPathItem *v, JsonPathItem *a)
Definition: jsonpath.c:992
#define Assert(condition)
Definition: c.h:732
JsonPathItemType type
Definition: jsonpath.h:107
JsonPathItemType type
Definition: jsonpath.h:188
#define PG_RETURN_CSTRING(x)
Definition: fmgr.h:352
Numeric numeric
Definition: jsonpath.h:231
struct JsonPathParseItem::@137::@138 args
#define JSONPATH_HDRSZ
Definition: jsonpath.h:30
char data[FLEXIBLE_ARRAY_MEMBER]
Definition: jsonpath.h:25
static int flattenJsonPathParseItem(StringInfo buf, JsonPathParseItem *item, int nestingLevel, bool insideArraySubscript)
Definition: jsonpath.c:229
static int32 reserveSpaceForItemPointer(StringInfo buf)
Definition: jsonpath.c:462
struct JsonPathItem::@130::@135 like_regex
static int operationPriority(JsonPathItemType op)
Definition: jsonpath.c:777
Datum jsonpath_out(PG_FUNCTION_ARGS)
Definition: jsonpath.c:129
int errmsg(const char *fmt,...)
Definition: elog.c:784
void jspInit(JsonPathItem *v, JsonPath *js)
Definition: jsonpath.c:833
#define elog(elevel,...)
Definition: elog.h:226
int i
struct JsonPathItem::@130::@131 args
void * arg
#define PG_GETARG_CSTRING(n)
Definition: fmgr.h:272
uint32 header
Definition: jsonpath.h:24
#define PG_FUNCTION_ARGS
Definition: fmgr.h:188
#define read_byte(v, b, p)
Definition: jsonpath.c:814
unsigned int pq_getmsgint(StringInfo msg, int b)
Definition: pqformat.c:417
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:99
static void printJsonPathItem(StringInfo buf, JsonPathItem *v, bool inKey, bool printBracketes)
Definition: jsonpath.c:476
struct JsonPathParseItem::@137::@140 anybounds
#define SET_VARSIZE(PTR, len)
Definition: postgres.h:329
union JsonPathParseItem::@137 value
static void pq_sendint8(StringInfo buf, uint8 i)
Definition: pqformat.h:129
void jspGetArg(JsonPathItem *v, JsonPathItem *a)
Definition: jsonpath.c:923
char * jspGetString(JsonPathItem *v, int32 *len)
Definition: jsonpath.c:1050
int32 nextPos
Definition: jsonpath.h:110
void appendBinaryStringInfo(StringInfo str, const char *data, int datalen)
Definition: stringinfo.c:214
struct JsonPathItem::@130::@132 array
Numeric jspGetNumeric(JsonPathItem *v)
Definition: jsonpath.c:1042