PostgreSQL Source Code git master
jsonb_gin.c
Go to the documentation of this file.
1/*-------------------------------------------------------------------------
2 *
3 * jsonb_gin.c
4 * GIN support functions for jsonb
5 *
6 * Copyright (c) 2014-2025, PostgreSQL Global Development Group
7 *
8 * We provide two opclasses for jsonb indexing: jsonb_ops and jsonb_path_ops.
9 * For their description see json.sgml and comments in jsonb.h.
10 *
11 * The operators support, among the others, "jsonb @? jsonpath" and
12 * "jsonb @@ jsonpath". Expressions containing these operators are easily
13 * expressed through each other.
14 *
15 * jb @? 'path' <=> jb @@ 'EXISTS(path)'
16 * jb @@ 'expr' <=> jb @? '$ ? (expr)'
17 *
18 * Thus, we're going to consider only @@ operator, while regarding @? operator
19 * the same is true for jb @@ 'EXISTS(path)'.
20 *
21 * Result of jsonpath query extraction is a tree, which leaf nodes are index
22 * entries and non-leaf nodes are AND/OR logical expressions. Basically we
23 * extract following statements out of jsonpath:
24 *
25 * 1) "accessors_chain = const",
26 * 2) "EXISTS(accessors_chain)".
27 *
28 * Accessors chain may consist of .key, [*] and [index] accessors. jsonb_ops
29 * additionally supports .* and .**.
30 *
31 * For now, both jsonb_ops and jsonb_path_ops supports only statements of
32 * the 1st find. jsonb_ops might also support statements of the 2nd kind,
33 * but given we have no statistics keys extracted from accessors chain
34 * are likely non-selective. Therefore, we choose to not confuse optimizer
35 * and skip statements of the 2nd kind altogether. In future versions that
36 * might be changed.
37 *
38 * In jsonb_ops statement of the 1st kind is split into expression of AND'ed
39 * keys and const. Sometimes const might be interpreted as both value or key
40 * in jsonb_ops. Then statement of 1st kind is decomposed into the expression
41 * below.
42 *
43 * key1 AND key2 AND ... AND keyN AND (const_as_value OR const_as_key)
44 *
45 * jsonb_path_ops transforms each statement of the 1st kind into single hash
46 * entry below.
47 *
48 * HASH(key1, key2, ... , keyN, const)
49 *
50 * Despite statements of the 2nd kind are not supported by both jsonb_ops and
51 * jsonb_path_ops, EXISTS(path) expressions might be still supported,
52 * when statements of 1st kind could be extracted out of their filters.
53 *
54 * IDENTIFICATION
55 * src/backend/utils/adt/jsonb_gin.c
56 *
57 *-------------------------------------------------------------------------
58 */
59
60#include "postgres.h"
61
62#include "access/gin.h"
63#include "access/stratnum.h"
65#include "catalog/pg_type.h"
66#include "common/hashfn.h"
67#include "miscadmin.h"
68#include "utils/fmgrprotos.h"
69#include "utils/jsonb.h"
70#include "utils/jsonpath.h"
71#include "utils/varlena.h"
72
73typedef struct PathHashStack
74{
78
79/* Buffer for GIN entries */
80typedef struct GinEntries
81{
83 int count;
86
88{
93
95
96/* Node in jsonpath expression tree */
98{
100 union
101 {
102 int nargs; /* valid for OR and AND nodes */
103 int entryIndex; /* index in GinEntries array, valid for ENTRY
104 * nodes after entries output */
105 Datum entryDatum; /* path hash or key name/scalar, valid for
106 * ENTRY nodes before entries output */
108 JsonPathGinNode *args[FLEXIBLE_ARRAY_MEMBER]; /* valid for OR and AND
109 * nodes */
110};
111
112/*
113 * jsonb_ops entry extracted from jsonpath item. Corresponding path item
114 * may be: '.key', '.*', '.**', '[index]' or '[*]'.
115 * Entry type is stored in 'type' field.
116 */
118{
120 Datum keyName; /* key name (for '.key' path item) or NULL */
121 JsonPathItemType type; /* type of jsonpath item */
123
124/* GIN representation of the extracted json path */
125typedef union JsonPathGinPath
126{
127 JsonPathGinPathItem *items; /* list of path items (jsonb_ops) */
128 uint32 hash; /* hash of the path (jsonb_path_ops) */
130
132
133/* Callback, which stores information about path item into JsonPathGinPath */
135 JsonPathItem *jsp);
136
137/*
138 * Callback, which extracts set of nodes from statement of 1st kind
139 * (scalar != NULL) or statement of 2nd kind (scalar == NULL).
140 */
141typedef List *(*JsonPathGinExtractNodesFunc) (JsonPathGinContext *cxt,
142 JsonPathGinPath path,
143 JsonbValue *scalar,
144 List *nodes);
145
146/* Context for jsonpath entries extraction */
148{
151 bool lax;
152};
153
154static Datum make_text_key(char flag, const char *str, int len);
155static Datum make_scalar_key(const JsonbValue *scalarVal, bool is_key);
156
158 JsonPathGinPath path, JsonPathItem *jsp, bool not);
159
160
161/* Initialize GinEntries struct */
162static void
163init_gin_entries(GinEntries *entries, int preallocated)
164{
165 entries->allocated = preallocated;
166 entries->buf = preallocated ? palloc_array(Datum, preallocated) : NULL;
167 entries->count = 0;
168}
169
170/* Add new entry to GinEntries */
171static int
173{
174 int id = entries->count;
175
176 if (entries->count >= entries->allocated)
177 {
178 if (entries->allocated)
179 {
180 entries->allocated *= 2;
181 entries->buf = repalloc_array(entries->buf,
182 Datum,
183 entries->allocated);
184 }
185 else
186 {
187 entries->allocated = 8;
188 entries->buf = palloc_array(Datum, entries->allocated);
189 }
190 }
191
192 entries->buf[entries->count++] = entry;
193
194 return id;
195}
196
197/*
198 *
199 * jsonb_ops GIN opclass support functions
200 *
201 */
202
203Datum
205{
206 text *arg1 = PG_GETARG_TEXT_PP(0);
207 text *arg2 = PG_GETARG_TEXT_PP(1);
208 int32 result;
209 char *a1p,
210 *a2p;
211 int len1,
212 len2;
213
214 a1p = VARDATA_ANY(arg1);
215 a2p = VARDATA_ANY(arg2);
216
217 len1 = VARSIZE_ANY_EXHDR(arg1);
218 len2 = VARSIZE_ANY_EXHDR(arg2);
219
220 /* Compare text as bttextcmp does, but always using C collation */
221 result = varstr_cmp(a1p, len1, a2p, len2, C_COLLATION_OID);
222
223 PG_FREE_IF_COPY(arg1, 0);
224 PG_FREE_IF_COPY(arg2, 1);
225
226 PG_RETURN_INT32(result);
227}
228
229Datum
231{
232 Jsonb *jb = (Jsonb *) PG_GETARG_JSONB_P(0);
233 int32 *nentries = (int32 *) PG_GETARG_POINTER(1);
234 int total = JB_ROOT_COUNT(jb);
235 JsonbIterator *it;
236 JsonbValue v;
238 GinEntries entries;
239
240 /* If the root level is empty, we certainly have no keys */
241 if (total == 0)
242 {
243 *nentries = 0;
244 PG_RETURN_POINTER(NULL);
245 }
246
247 /* Otherwise, use 2 * root count as initial estimate of result size */
248 init_gin_entries(&entries, 2 * total);
249
250 it = JsonbIteratorInit(&jb->root);
251
252 while ((r = JsonbIteratorNext(&it, &v, false)) != WJB_DONE)
253 {
254 switch (r)
255 {
256 case WJB_KEY:
257 add_gin_entry(&entries, make_scalar_key(&v, true));
258 break;
259 case WJB_ELEM:
260 /* Pretend string array elements are keys, see jsonb.h */
261 add_gin_entry(&entries, make_scalar_key(&v, v.type == jbvString));
262 break;
263 case WJB_VALUE:
264 add_gin_entry(&entries, make_scalar_key(&v, false));
265 break;
266 default:
267 /* we can ignore structural items */
268 break;
269 }
270 }
271
272 *nentries = entries.count;
273
274 PG_RETURN_POINTER(entries.buf);
275}
276
277/* Append JsonPathGinPathItem to JsonPathGinPath (jsonb_ops) */
278static bool
280{
281 JsonPathGinPathItem *pentry;
282 Datum keyName;
283
284 switch (jsp->type)
285 {
286 case jpiRoot:
287 path->items = NULL; /* reset path */
288 return true;
289
290 case jpiKey:
291 {
292 int len;
293 char *key = jspGetString(jsp, &len);
294
295 keyName = make_text_key(JGINFLAG_KEY, key, len);
296 break;
297 }
298
299 case jpiAny:
300 case jpiAnyKey:
301 case jpiAnyArray:
302 case jpiIndexArray:
303 keyName = PointerGetDatum(NULL);
304 break;
305
306 default:
307 /* other path items like item methods are not supported */
308 return false;
309 }
310
312
313 pentry->type = jsp->type;
314 pentry->keyName = keyName;
315 pentry->parent = path->items;
316
317 path->items = pentry;
318
319 return true;
320}
321
322/* Combine existing path hash with next key hash (jsonb_path_ops) */
323static bool
325{
326 switch (jsp->type)
327 {
328 case jpiRoot:
329 path->hash = 0; /* reset path hash */
330 return true;
331
332 case jpiKey:
333 {
334 JsonbValue jbv;
335
336 jbv.type = jbvString;
337 jbv.val.string.val = jspGetString(jsp, &jbv.val.string.len);
338
339 JsonbHashScalarValue(&jbv, &path->hash);
340 return true;
341 }
342
343 case jpiIndexArray:
344 case jpiAnyArray:
345 return true; /* path hash is unchanged */
346
347 default:
348 /* other items (wildcard paths, item methods) are not supported */
349 return false;
350 }
351}
352
353static JsonPathGinNode *
355{
356 JsonPathGinNode *node = palloc(offsetof(JsonPathGinNode, args));
357
358 node->type = JSP_GIN_ENTRY;
359 node->val.entryDatum = entry;
360
361 return node;
362}
363
364static JsonPathGinNode *
366{
367 return make_jsp_entry_node(make_scalar_key(scalar, iskey));
368}
369
370static JsonPathGinNode *
372{
373 JsonPathGinNode *node = palloc(offsetof(JsonPathGinNode, args) +
374 sizeof(node->args[0]) * nargs);
375
376 node->type = type;
377 node->val.nargs = nargs;
378
379 return node;
380}
381
382static JsonPathGinNode *
384{
386 ListCell *lc;
387 int i = 0;
388
389 foreach(lc, args)
390 node->args[i++] = lfirst(lc);
391
392 return node;
393}
394
395static JsonPathGinNode *
397 JsonPathGinNode *arg1, JsonPathGinNode *arg2)
398{
400
401 node->args[0] = arg1;
402 node->args[1] = arg2;
403
404 return node;
405}
406
407/* Append a list of nodes from the jsonpath (jsonb_ops). */
408static List *
410 JsonbValue *scalar, List *nodes)
411{
412 JsonPathGinPathItem *pentry;
413
414 if (scalar)
415 {
416 JsonPathGinNode *node;
417
418 /*
419 * Append path entry nodes only if scalar is provided. See header
420 * comment for details.
421 */
422 for (pentry = path.items; pentry; pentry = pentry->parent)
423 {
424 if (pentry->type == jpiKey) /* only keys are indexed */
425 nodes = lappend(nodes, make_jsp_entry_node(pentry->keyName));
426 }
427
428 /* Append scalar node for equality queries. */
429 if (scalar->type == jbvString)
430 {
431 JsonPathGinPathItem *last = path.items;
432 GinTernaryValue key_entry;
433
434 /*
435 * Assuming that jsonb_ops interprets string array elements as
436 * keys, we may extract key or non-key entry or even both. In the
437 * latter case we create OR-node. It is possible in lax mode
438 * where arrays are automatically unwrapped, or in strict mode for
439 * jpiAny items.
440 */
441
442 if (cxt->lax)
443 key_entry = GIN_MAYBE;
444 else if (!last) /* root ($) */
445 key_entry = GIN_FALSE;
446 else if (last->type == jpiAnyArray || last->type == jpiIndexArray)
447 key_entry = GIN_TRUE;
448 else if (last->type == jpiAny)
449 key_entry = GIN_MAYBE;
450 else
451 key_entry = GIN_FALSE;
452
453 if (key_entry == GIN_MAYBE)
454 {
456 JsonPathGinNode *n2 = make_jsp_entry_node_scalar(scalar, false);
457
459 }
460 else
461 {
462 node = make_jsp_entry_node_scalar(scalar,
463 key_entry == GIN_TRUE);
464 }
465 }
466 else
467 {
468 node = make_jsp_entry_node_scalar(scalar, false);
469 }
470
471 nodes = lappend(nodes, node);
472 }
473
474 return nodes;
475}
476
477/* Append a list of nodes from the jsonpath (jsonb_path_ops). */
478static List *
480 JsonbValue *scalar, List *nodes)
481{
482 if (scalar)
483 {
484 /* append path hash node for equality queries */
485 uint32 hash = path.hash;
486
487 JsonbHashScalarValue(scalar, &hash);
488
489 return lappend(nodes,
491 }
492 else
493 {
494 /* jsonb_path_ops doesn't support EXISTS queries => nothing to append */
495 return nodes;
496 }
497}
498
499/*
500 * Extract a list of expression nodes that need to be AND-ed by the caller.
501 * Extracted expression is 'path == scalar' if 'scalar' is non-NULL, and
502 * 'EXISTS(path)' otherwise.
503 */
504static List *
506 JsonPathItem *jsp, JsonbValue *scalar)
507{
509 List *nodes = NIL;
510
511 for (;;)
512 {
513 switch (jsp->type)
514 {
515 case jpiCurrent:
516 break;
517
518 case jpiFilter:
519 {
521 JsonPathGinNode *filter;
522
523 jspGetArg(jsp, &arg);
524
525 filter = extract_jsp_bool_expr(cxt, path, &arg, false);
526
527 if (filter)
528 nodes = lappend(nodes, filter);
529
530 break;
531 }
532
533 default:
534 if (!cxt->add_path_item(&path, jsp))
535
536 /*
537 * Path is not supported by the index opclass, return only
538 * the extracted filter nodes.
539 */
540 return nodes;
541 break;
542 }
543
544 if (!jspGetNext(jsp, &next))
545 break;
546
547 jsp = &next;
548 }
549
550 /*
551 * Append nodes from the path expression itself to the already extracted
552 * list of filter nodes.
553 */
554 return cxt->extract_nodes(cxt, path, scalar, nodes);
555}
556
557/*
558 * Extract an expression node from one of following jsonpath path expressions:
559 * EXISTS(jsp) (when 'scalar' is NULL)
560 * jsp == scalar (when 'scalar' is not NULL).
561 *
562 * The current path (@) is passed in 'path'.
563 */
564static JsonPathGinNode *
566 JsonPathItem *jsp, JsonbValue *scalar)
567{
568 /* extract a list of nodes to be AND-ed */
569 List *nodes = extract_jsp_path_expr_nodes(cxt, path, jsp, scalar);
570
571 if (nodes == NIL)
572 /* no nodes were extracted => full scan is needed for this path */
573 return NULL;
574
575 if (list_length(nodes) == 1)
576 return linitial(nodes); /* avoid extra AND-node */
577
578 /* construct AND-node for path with filters */
580}
581
582/* Recursively extract nodes from the boolean jsonpath expression. */
583static JsonPathGinNode *
585 JsonPathItem *jsp, bool not)
586{
588
589 switch (jsp->type)
590 {
591 case jpiAnd: /* expr && expr */
592 case jpiOr: /* expr || expr */
593 {
595 JsonPathGinNode *larg;
596 JsonPathGinNode *rarg;
598
599 jspGetLeftArg(jsp, &arg);
600 larg = extract_jsp_bool_expr(cxt, path, &arg, not);
601
602 jspGetRightArg(jsp, &arg);
603 rarg = extract_jsp_bool_expr(cxt, path, &arg, not);
604
605 if (!larg || !rarg)
606 {
607 if (jsp->type == jpiOr)
608 return NULL;
609
610 return larg ? larg : rarg;
611 }
612
613 type = not ^ (jsp->type == jpiAnd) ? JSP_GIN_AND : JSP_GIN_OR;
614
615 return make_jsp_expr_node_binary(type, larg, rarg);
616 }
617
618 case jpiNot: /* !expr */
619 {
621
622 jspGetArg(jsp, &arg);
623
624 /* extract child expression inverting 'not' flag */
625 return extract_jsp_bool_expr(cxt, path, &arg, !not);
626 }
627
628 case jpiExists: /* EXISTS(path) */
629 {
631
632 if (not)
633 return NULL; /* NOT EXISTS is not supported */
634
635 jspGetArg(jsp, &arg);
636
637 return extract_jsp_path_expr(cxt, path, &arg, NULL);
638 }
639
640 case jpiNotEqual:
641
642 /*
643 * 'not' == true case is not supported here because '!(path !=
644 * scalar)' is not equivalent to 'path == scalar' in the general
645 * case because of sequence comparison semantics: 'path == scalar'
646 * === 'EXISTS (path, @ == scalar)', '!(path != scalar)' ===
647 * 'FOR_ALL(path, @ == scalar)'. So, we should translate '!(path
648 * != scalar)' into GIN query 'path == scalar || EMPTY(path)', but
649 * 'EMPTY(path)' queries are not supported by the both jsonb
650 * opclasses. However in strict mode we could omit 'EMPTY(path)'
651 * part if the path can return exactly one item (it does not
652 * contain wildcard accessors or item methods like .keyvalue()
653 * etc.).
654 */
655 return NULL;
656
657 case jpiEqual: /* path == scalar */
658 {
659 JsonPathItem left_item;
660 JsonPathItem right_item;
661 JsonPathItem *path_item;
662 JsonPathItem *scalar_item;
663 JsonbValue scalar;
664
665 if (not)
666 return NULL;
667
668 jspGetLeftArg(jsp, &left_item);
669 jspGetRightArg(jsp, &right_item);
670
671 if (jspIsScalar(left_item.type))
672 {
673 scalar_item = &left_item;
674 path_item = &right_item;
675 }
676 else if (jspIsScalar(right_item.type))
677 {
678 scalar_item = &right_item;
679 path_item = &left_item;
680 }
681 else
682 return NULL; /* at least one operand should be a scalar */
683
684 switch (scalar_item->type)
685 {
686 case jpiNull:
687 scalar.type = jbvNull;
688 break;
689 case jpiBool:
690 scalar.type = jbvBool;
691 scalar.val.boolean = !!*scalar_item->content.value.data;
692 break;
693 case jpiNumeric:
694 scalar.type = jbvNumeric;
695 scalar.val.numeric =
696 (Numeric) scalar_item->content.value.data;
697 break;
698 case jpiString:
699 scalar.type = jbvString;
700 scalar.val.string.val = scalar_item->content.value.data;
701 scalar.val.string.len =
702 scalar_item->content.value.datalen;
703 break;
704 default:
705 elog(ERROR, "invalid scalar jsonpath item type: %d",
706 scalar_item->type);
707 return NULL;
708 }
709
710 return extract_jsp_path_expr(cxt, path, path_item, &scalar);
711 }
712
713 default:
714 return NULL; /* not a boolean expression */
715 }
716}
717
718/* Recursively emit all GIN entries found in the node tree */
719static void
721{
723
724 switch (node->type)
725 {
726 case JSP_GIN_ENTRY:
727 /* replace datum with its index in the array */
728 node->val.entryIndex = add_gin_entry(entries, node->val.entryDatum);
729 break;
730
731 case JSP_GIN_OR:
732 case JSP_GIN_AND:
733 {
734 int i;
735
736 for (i = 0; i < node->val.nargs; i++)
737 emit_jsp_gin_entries(node->args[i], entries);
738
739 break;
740 }
741 }
742}
743
744/*
745 * Recursively extract GIN entries from jsonpath query.
746 * Root expression node is put into (*extra_data)[0].
747 */
748static Datum *
750 int32 *nentries, Pointer **extra_data)
751{
754 JsonPathGinNode *node;
755 JsonPathGinPath path = {0};
756 GinEntries entries = {0};
757
758 cxt.lax = (jp->header & JSONPATH_LAX) != 0;
759
760 if (pathOps)
761 {
764 }
765 else
766 {
769 }
770
771 jspInit(&root, jp);
772
774 ? extract_jsp_path_expr(&cxt, path, &root, NULL)
775 : extract_jsp_bool_expr(&cxt, path, &root, false);
776
777 if (!node)
778 {
779 *nentries = 0;
780 return NULL;
781 }
782
783 emit_jsp_gin_entries(node, &entries);
784
785 *nentries = entries.count;
786 if (!*nentries)
787 return NULL;
788
789 *extra_data = palloc0_array(Pointer, entries.count);
790 **extra_data = (Pointer) node;
791
792 return entries.buf;
793}
794
795/*
796 * Recursively execute jsonpath expression.
797 * 'check' is a bool[] or a GinTernaryValue[] depending on 'ternary' flag.
798 */
799static GinTernaryValue
800execute_jsp_gin_node(JsonPathGinNode *node, void *check, bool ternary)
801{
802 GinTernaryValue res;
804 int i;
805
806 switch (node->type)
807 {
808 case JSP_GIN_AND:
809 res = GIN_TRUE;
810 for (i = 0; i < node->val.nargs; i++)
811 {
812 v = execute_jsp_gin_node(node->args[i], check, ternary);
813 if (v == GIN_FALSE)
814 return GIN_FALSE;
815 else if (v == GIN_MAYBE)
816 res = GIN_MAYBE;
817 }
818 return res;
819
820 case JSP_GIN_OR:
821 res = GIN_FALSE;
822 for (i = 0; i < node->val.nargs; i++)
823 {
824 v = execute_jsp_gin_node(node->args[i], check, ternary);
825 if (v == GIN_TRUE)
826 return GIN_TRUE;
827 else if (v == GIN_MAYBE)
828 res = GIN_MAYBE;
829 }
830 return res;
831
832 case JSP_GIN_ENTRY:
833 {
834 int index = node->val.entryIndex;
835
836 if (ternary)
837 return ((GinTernaryValue *) check)[index];
838 else
839 return ((bool *) check)[index] ? GIN_TRUE : GIN_FALSE;
840 }
841
842 default:
843 elog(ERROR, "invalid jsonpath gin node type: %d", node->type);
844 return GIN_FALSE; /* keep compiler quiet */
845 }
846}
847
848Datum
850{
851 int32 *nentries = (int32 *) PG_GETARG_POINTER(1);
852 StrategyNumber strategy = PG_GETARG_UINT16(2);
853 int32 *searchMode = (int32 *) PG_GETARG_POINTER(6);
854 Datum *entries;
855
856 if (strategy == JsonbContainsStrategyNumber)
857 {
858 /* Query is a jsonb, so just apply gin_extract_jsonb... */
859 entries = (Datum *)
862 PointerGetDatum(nentries)));
863 /* ...although "contains {}" requires a full index scan */
864 if (*nentries == 0)
865 *searchMode = GIN_SEARCH_MODE_ALL;
866 }
867 else if (strategy == JsonbExistsStrategyNumber)
868 {
869 /* Query is a text string, which we treat as a key */
870 text *query = PG_GETARG_TEXT_PP(0);
871
872 *nentries = 1;
873 entries = palloc_object(Datum);
874 entries[0] = make_text_key(JGINFLAG_KEY,
875 VARDATA_ANY(query),
876 VARSIZE_ANY_EXHDR(query));
877 }
878 else if (strategy == JsonbExistsAnyStrategyNumber ||
880 {
881 /* Query is a text array; each element is treated as a key */
883 Datum *key_datums;
884 bool *key_nulls;
885 int key_count;
886 int i,
887 j;
888
889 deconstruct_array_builtin(query, TEXTOID, &key_datums, &key_nulls, &key_count);
890
891 entries = palloc_array(Datum, key_count);
892
893 for (i = 0, j = 0; i < key_count; i++)
894 {
895 /* Nulls in the array are ignored */
896 if (key_nulls[i])
897 continue;
898 /* We rely on the array elements not being toasted */
899 entries[j++] = make_text_key(JGINFLAG_KEY,
900 VARDATA_ANY(DatumGetPointer(key_datums[i])),
901 VARSIZE_ANY_EXHDR(DatumGetPointer(key_datums[i])));
902 }
903
904 *nentries = j;
905 /* ExistsAll with no keys should match everything */
906 if (j == 0 && strategy == JsonbExistsAllStrategyNumber)
907 *searchMode = GIN_SEARCH_MODE_ALL;
908 }
909 else if (strategy == JsonbJsonpathPredicateStrategyNumber ||
911 {
913 Pointer **extra_data = (Pointer **) PG_GETARG_POINTER(4);
914
915 entries = extract_jsp_query(jp, strategy, false, nentries, extra_data);
916
917 if (!entries)
918 *searchMode = GIN_SEARCH_MODE_ALL;
919 }
920 else
921 {
922 elog(ERROR, "unrecognized strategy number: %d", strategy);
923 entries = NULL; /* keep compiler quiet */
924 }
925
926 PG_RETURN_POINTER(entries);
927}
928
929Datum
931{
932 bool *check = (bool *) PG_GETARG_POINTER(0);
933 StrategyNumber strategy = PG_GETARG_UINT16(1);
934
935 /* Jsonb *query = PG_GETARG_JSONB_P(2); */
936 int32 nkeys = PG_GETARG_INT32(3);
937
938 Pointer *extra_data = (Pointer *) PG_GETARG_POINTER(4);
939 bool *recheck = (bool *) PG_GETARG_POINTER(5);
940 bool res = true;
941 int32 i;
942
943 if (strategy == JsonbContainsStrategyNumber)
944 {
945 /*
946 * We must always recheck, since we can't tell from the index whether
947 * the positions of the matched items match the structure of the query
948 * object. (Even if we could, we'd also have to worry about hashed
949 * keys and the index's failure to distinguish keys from string array
950 * elements.) However, the tuple certainly doesn't match unless it
951 * contains all the query keys.
952 */
953 *recheck = true;
954 for (i = 0; i < nkeys; i++)
955 {
956 if (!check[i])
957 {
958 res = false;
959 break;
960 }
961 }
962 }
963 else if (strategy == JsonbExistsStrategyNumber)
964 {
965 /*
966 * Although the key is certainly present in the index, we must recheck
967 * because (1) the key might be hashed, and (2) the index match might
968 * be for a key that's not at top level of the JSON object. For (1),
969 * we could look at the query key to see if it's hashed and not
970 * recheck if not, but the index lacks enough info to tell about (2).
971 */
972 *recheck = true;
973 res = true;
974 }
975 else if (strategy == JsonbExistsAnyStrategyNumber)
976 {
977 /* As for plain exists, we must recheck */
978 *recheck = true;
979 res = true;
980 }
981 else if (strategy == JsonbExistsAllStrategyNumber)
982 {
983 /* As for plain exists, we must recheck */
984 *recheck = true;
985 /* ... but unless all the keys are present, we can say "false" */
986 for (i = 0; i < nkeys; i++)
987 {
988 if (!check[i])
989 {
990 res = false;
991 break;
992 }
993 }
994 }
995 else if (strategy == JsonbJsonpathPredicateStrategyNumber ||
997 {
998 *recheck = true;
999
1000 if (nkeys > 0)
1001 {
1002 Assert(extra_data && extra_data[0]);
1003 res = execute_jsp_gin_node(extra_data[0], check, false) != GIN_FALSE;
1004 }
1005 }
1006 else
1007 elog(ERROR, "unrecognized strategy number: %d", strategy);
1008
1009 PG_RETURN_BOOL(res);
1010}
1011
1012Datum
1014{
1016 StrategyNumber strategy = PG_GETARG_UINT16(1);
1017
1018 /* Jsonb *query = PG_GETARG_JSONB_P(2); */
1019 int32 nkeys = PG_GETARG_INT32(3);
1020 Pointer *extra_data = (Pointer *) PG_GETARG_POINTER(4);
1022 int32 i;
1023
1024 /*
1025 * Note that we never return GIN_TRUE, only GIN_MAYBE or GIN_FALSE; this
1026 * corresponds to always forcing recheck in the regular consistent
1027 * function, for the reasons listed there.
1028 */
1029 if (strategy == JsonbContainsStrategyNumber ||
1030 strategy == JsonbExistsAllStrategyNumber)
1031 {
1032 /* All extracted keys must be present */
1033 for (i = 0; i < nkeys; i++)
1034 {
1035 if (check[i] == GIN_FALSE)
1036 {
1037 res = GIN_FALSE;
1038 break;
1039 }
1040 }
1041 }
1042 else if (strategy == JsonbExistsStrategyNumber ||
1043 strategy == JsonbExistsAnyStrategyNumber)
1044 {
1045 /* At least one extracted key must be present */
1046 res = GIN_FALSE;
1047 for (i = 0; i < nkeys; i++)
1048 {
1049 if (check[i] == GIN_TRUE ||
1050 check[i] == GIN_MAYBE)
1051 {
1052 res = GIN_MAYBE;
1053 break;
1054 }
1055 }
1056 }
1057 else if (strategy == JsonbJsonpathPredicateStrategyNumber ||
1059 {
1060 if (nkeys > 0)
1061 {
1062 Assert(extra_data && extra_data[0]);
1063 res = execute_jsp_gin_node(extra_data[0], check, true);
1064
1065 /* Should always recheck the result */
1066 if (res == GIN_TRUE)
1067 res = GIN_MAYBE;
1068 }
1069 }
1070 else
1071 elog(ERROR, "unrecognized strategy number: %d", strategy);
1072
1074}
1075
1076/*
1077 *
1078 * jsonb_path_ops GIN opclass support functions
1079 *
1080 * In a jsonb_path_ops index, the GIN keys are uint32 hashes, one per JSON
1081 * value; but the JSON key(s) leading to each value are also included in its
1082 * hash computation. This means we can only support containment queries,
1083 * but the index can distinguish, for example, {"foo": 42} from {"bar": 42}
1084 * since different hashes will be generated.
1085 *
1086 */
1087
1088Datum
1090{
1091 Jsonb *jb = PG_GETARG_JSONB_P(0);
1092 int32 *nentries = (int32 *) PG_GETARG_POINTER(1);
1093 int total = JB_ROOT_COUNT(jb);
1094 JsonbIterator *it;
1095 JsonbValue v;
1097 PathHashStack tail;
1098 PathHashStack *stack;
1099 GinEntries entries;
1100
1101 /* If the root level is empty, we certainly have no keys */
1102 if (total == 0)
1103 {
1104 *nentries = 0;
1105 PG_RETURN_POINTER(NULL);
1106 }
1107
1108 /* Otherwise, use 2 * root count as initial estimate of result size */
1109 init_gin_entries(&entries, 2 * total);
1110
1111 /* We keep a stack of partial hashes corresponding to parent key levels */
1112 tail.parent = NULL;
1113 tail.hash = 0;
1114 stack = &tail;
1115
1116 it = JsonbIteratorInit(&jb->root);
1117
1118 while ((r = JsonbIteratorNext(&it, &v, false)) != WJB_DONE)
1119 {
1120 PathHashStack *parent;
1121
1122 switch (r)
1123 {
1124 case WJB_BEGIN_ARRAY:
1125 case WJB_BEGIN_OBJECT:
1126 /* Push a stack level for this object */
1127 parent = stack;
1129
1130 /*
1131 * We pass forward hashes from outer nesting levels so that
1132 * the hashes for nested values will include outer keys as
1133 * well as their own keys.
1134 *
1135 * Nesting an array within another array will not alter
1136 * innermost scalar element hash values, but that seems
1137 * inconsequential.
1138 */
1139 stack->hash = parent->hash;
1140 stack->parent = parent;
1141 break;
1142 case WJB_KEY:
1143 /* mix this key into the current outer hash */
1144 JsonbHashScalarValue(&v, &stack->hash);
1145 /* hash is now ready to incorporate the value */
1146 break;
1147 case WJB_ELEM:
1148 case WJB_VALUE:
1149 /* mix the element or value's hash into the prepared hash */
1150 JsonbHashScalarValue(&v, &stack->hash);
1151 /* and emit an index entry */
1152 add_gin_entry(&entries, UInt32GetDatum(stack->hash));
1153 /* reset hash for next key, value, or sub-object */
1154 stack->hash = stack->parent->hash;
1155 break;
1156 case WJB_END_ARRAY:
1157 case WJB_END_OBJECT:
1158 /* Pop the stack */
1159 parent = stack->parent;
1160 pfree(stack);
1161 stack = parent;
1162 /* reset hash for next key, value, or sub-object */
1163 if (stack->parent)
1164 stack->hash = stack->parent->hash;
1165 else
1166 stack->hash = 0;
1167 break;
1168 default:
1169 elog(ERROR, "invalid JsonbIteratorNext rc: %d", (int) r);
1170 }
1171 }
1172
1173 *nentries = entries.count;
1174
1175 PG_RETURN_POINTER(entries.buf);
1176}
1177
1178Datum
1180{
1181 int32 *nentries = (int32 *) PG_GETARG_POINTER(1);
1182 StrategyNumber strategy = PG_GETARG_UINT16(2);
1183 int32 *searchMode = (int32 *) PG_GETARG_POINTER(6);
1184 Datum *entries;
1185
1186 if (strategy == JsonbContainsStrategyNumber)
1187 {
1188 /* Query is a jsonb, so just apply gin_extract_jsonb_path ... */
1189 entries = (Datum *)
1191 PG_GETARG_DATUM(0),
1192 PointerGetDatum(nentries)));
1193
1194 /* ... although "contains {}" requires a full index scan */
1195 if (*nentries == 0)
1196 *searchMode = GIN_SEARCH_MODE_ALL;
1197 }
1198 else if (strategy == JsonbJsonpathPredicateStrategyNumber ||
1200 {
1202 Pointer **extra_data = (Pointer **) PG_GETARG_POINTER(4);
1203
1204 entries = extract_jsp_query(jp, strategy, true, nentries, extra_data);
1205
1206 if (!entries)
1207 *searchMode = GIN_SEARCH_MODE_ALL;
1208 }
1209 else
1210 {
1211 elog(ERROR, "unrecognized strategy number: %d", strategy);
1212 entries = NULL;
1213 }
1214
1215 PG_RETURN_POINTER(entries);
1216}
1217
1218Datum
1220{
1221 bool *check = (bool *) PG_GETARG_POINTER(0);
1222 StrategyNumber strategy = PG_GETARG_UINT16(1);
1223
1224 /* Jsonb *query = PG_GETARG_JSONB_P(2); */
1225 int32 nkeys = PG_GETARG_INT32(3);
1226 Pointer *extra_data = (Pointer *) PG_GETARG_POINTER(4);
1227 bool *recheck = (bool *) PG_GETARG_POINTER(5);
1228 bool res = true;
1229 int32 i;
1230
1231 if (strategy == JsonbContainsStrategyNumber)
1232 {
1233 /*
1234 * jsonb_path_ops is necessarily lossy, not only because of hash
1235 * collisions but also because it doesn't preserve complete
1236 * information about the structure of the JSON object. Besides, there
1237 * are some special rules around the containment of raw scalars in
1238 * arrays that are not handled here. So we must always recheck a
1239 * match. However, if not all of the keys are present, the tuple
1240 * certainly doesn't match.
1241 */
1242 *recheck = true;
1243 for (i = 0; i < nkeys; i++)
1244 {
1245 if (!check[i])
1246 {
1247 res = false;
1248 break;
1249 }
1250 }
1251 }
1252 else if (strategy == JsonbJsonpathPredicateStrategyNumber ||
1254 {
1255 *recheck = true;
1256
1257 if (nkeys > 0)
1258 {
1259 Assert(extra_data && extra_data[0]);
1260 res = execute_jsp_gin_node(extra_data[0], check, false) != GIN_FALSE;
1261 }
1262 }
1263 else
1264 elog(ERROR, "unrecognized strategy number: %d", strategy);
1265
1266 PG_RETURN_BOOL(res);
1267}
1268
1269Datum
1271{
1273 StrategyNumber strategy = PG_GETARG_UINT16(1);
1274
1275 /* Jsonb *query = PG_GETARG_JSONB_P(2); */
1276 int32 nkeys = PG_GETARG_INT32(3);
1277 Pointer *extra_data = (Pointer *) PG_GETARG_POINTER(4);
1279 int32 i;
1280
1281 if (strategy == JsonbContainsStrategyNumber)
1282 {
1283 /*
1284 * Note that we never return GIN_TRUE, only GIN_MAYBE or GIN_FALSE;
1285 * this corresponds to always forcing recheck in the regular
1286 * consistent function, for the reasons listed there.
1287 */
1288 for (i = 0; i < nkeys; i++)
1289 {
1290 if (check[i] == GIN_FALSE)
1291 {
1292 res = GIN_FALSE;
1293 break;
1294 }
1295 }
1296 }
1297 else if (strategy == JsonbJsonpathPredicateStrategyNumber ||
1299 {
1300 if (nkeys > 0)
1301 {
1302 Assert(extra_data && extra_data[0]);
1303 res = execute_jsp_gin_node(extra_data[0], check, true);
1304
1305 /* Should always recheck the result */
1306 if (res == GIN_TRUE)
1307 res = GIN_MAYBE;
1308 }
1309 }
1310 else
1311 elog(ERROR, "unrecognized strategy number: %d", strategy);
1312
1314}
1315
1316/*
1317 * Construct a jsonb_ops GIN key from a flag byte and a textual representation
1318 * (which need not be null-terminated). This function is responsible
1319 * for hashing overlength text representations; it will add the
1320 * JGINFLAG_HASHED bit to the flag value if it does that.
1321 */
1322static Datum
1323make_text_key(char flag, const char *str, int len)
1324{
1325 text *item;
1326 char hashbuf[10];
1327
1328 if (len > JGIN_MAXLENGTH)
1329 {
1330 uint32 hashval;
1331
1332 hashval = DatumGetUInt32(hash_any((const unsigned char *) str, len));
1333 snprintf(hashbuf, sizeof(hashbuf), "%08x", hashval);
1334 str = hashbuf;
1335 len = 8;
1337 }
1338
1339 /*
1340 * Now build the text Datum. For simplicity we build a 4-byte-header
1341 * varlena text Datum here, but we expect it will get converted to short
1342 * header format when stored in the index.
1343 */
1344 item = (text *) palloc(VARHDRSZ + len + 1);
1345 SET_VARSIZE(item, VARHDRSZ + len + 1);
1346
1347 *VARDATA(item) = flag;
1348
1349 memcpy(VARDATA(item) + 1, str, len);
1350
1351 return PointerGetDatum(item);
1352}
1353
1354/*
1355 * Create a textual representation of a JsonbValue that will serve as a GIN
1356 * key in a jsonb_ops index. is_key is true if the JsonbValue is a key,
1357 * or if it is a string array element (since we pretend those are keys,
1358 * see jsonb.h).
1359 */
1360static Datum
1361make_scalar_key(const JsonbValue *scalarVal, bool is_key)
1362{
1363 Datum item;
1364 char *cstr;
1365
1366 switch (scalarVal->type)
1367 {
1368 case jbvNull:
1369 Assert(!is_key);
1370 item = make_text_key(JGINFLAG_NULL, "", 0);
1371 break;
1372 case jbvBool:
1373 Assert(!is_key);
1375 scalarVal->val.boolean ? "t" : "f", 1);
1376 break;
1377 case jbvNumeric:
1378 Assert(!is_key);
1379
1380 /*
1381 * A normalized textual representation, free of trailing zeroes,
1382 * is required so that numerically equal values will produce equal
1383 * strings.
1384 *
1385 * It isn't ideal that numerics are stored in a relatively bulky
1386 * textual format. However, it's a notationally convenient way of
1387 * storing a "union" type in the GIN B-Tree, and indexing Jsonb
1388 * strings takes precedence.
1389 */
1390 cstr = numeric_normalize(scalarVal->val.numeric);
1391 item = make_text_key(JGINFLAG_NUM, cstr, strlen(cstr));
1392 pfree(cstr);
1393 break;
1394 case jbvString:
1395 item = make_text_key(is_key ? JGINFLAG_KEY : JGINFLAG_STR,
1396 scalarVal->val.string.val,
1397 scalarVal->val.string.len);
1398 break;
1399 default:
1400 elog(ERROR, "unrecognized jsonb scalar type: %d", scalarVal->type);
1401 item = 0; /* keep compiler quiet */
1402 break;
1403 }
1404
1405 return item;
1406}
#define PG_GETARG_ARRAYTYPE_P(n)
Definition: array.h:263
void deconstruct_array_builtin(const ArrayType *array, Oid elmtype, Datum **elemsp, bool **nullsp, int *nelemsp)
Definition: arrayfuncs.c:3698
char * numeric_normalize(Numeric num)
Definition: numeric.c:1009
static int32 next
Definition: blutils.c:224
#define VARHDRSZ
Definition: c.h:711
#define FLEXIBLE_ARRAY_MEMBER
Definition: c.h:486
int32_t int32
Definition: c.h:548
uint32_t uint32
Definition: c.h:552
void * Pointer
Definition: c.h:543
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:226
#define palloc_object(type)
Definition: fe_memutils.h:74
#define repalloc_array(pointer, type, count)
Definition: fe_memutils.h:78
#define palloc_array(type, count)
Definition: fe_memutils.h:76
#define palloc0_array(type, count)
Definition: fe_memutils.h:77
#define PG_FREE_IF_COPY(ptr, n)
Definition: fmgr.h:260
#define PG_GETARG_TEXT_PP(n)
Definition: fmgr.h:309
#define DirectFunctionCall2(func, arg1, arg2)
Definition: fmgr.h:684
#define PG_GETARG_POINTER(n)
Definition: fmgr.h:276
#define PG_GETARG_DATUM(n)
Definition: fmgr.h:268
#define PG_GETARG_UINT16(n)
Definition: fmgr.h:272
#define PG_RETURN_INT32(x)
Definition: fmgr.h:354
#define PG_GETARG_INT32(n)
Definition: fmgr.h:269
#define PG_RETURN_POINTER(x)
Definition: fmgr.h:361
#define PG_FUNCTION_ARGS
Definition: fmgr.h:193
#define PG_RETURN_BOOL(x)
Definition: fmgr.h:359
#define PG_RETURN_GIN_TERNARY_VALUE(x)
Definition: gin.h:92
#define GIN_SEARCH_MODE_ALL
Definition: gin.h:38
#define GIN_FALSE
Definition: gin.h:76
char GinTernaryValue
Definition: gin.h:71
#define GIN_MAYBE
Definition: gin.h:78
#define GIN_TRUE
Definition: gin.h:77
static Datum hash_any(const unsigned char *k, int keylen)
Definition: hashfn.h:31
Assert(PointerIsAligned(start, uint64))
const char * str
int j
Definition: isn.c:78
int i
Definition: isn.c:77
@ jbvNumeric
Definition: jsonb.h:232
@ jbvBool
Definition: jsonb.h:233
@ jbvNull
Definition: jsonb.h:230
@ jbvString
Definition: jsonb.h:231
#define JGINFLAG_KEY
Definition: jsonb.h:62
#define JGINFLAG_STR
Definition: jsonb.h:66
#define JGINFLAG_NULL
Definition: jsonb.h:63
#define JGINFLAG_BOOL
Definition: jsonb.h:64
#define JGINFLAG_HASHED
Definition: jsonb.h:67
#define JGINFLAG_NUM
Definition: jsonb.h:65
#define PG_GETARG_JSONB_P(x)
Definition: jsonb.h:418
#define JsonbContainsStrategyNumber
Definition: jsonb.h:33
#define JGIN_MAXLENGTH
Definition: jsonb.h:68
#define JsonbJsonpathPredicateStrategyNumber
Definition: jsonb.h:38
#define JsonbExistsAllStrategyNumber
Definition: jsonb.h:36
#define JsonbExistsStrategyNumber
Definition: jsonb.h:34
#define JsonbExistsAnyStrategyNumber
Definition: jsonb.h:35
JsonbIteratorToken
Definition: jsonb.h:21
@ WJB_KEY
Definition: jsonb.h:23
@ WJB_DONE
Definition: jsonb.h:22
@ WJB_END_ARRAY
Definition: jsonb.h:27
@ WJB_VALUE
Definition: jsonb.h:24
@ WJB_END_OBJECT
Definition: jsonb.h:29
@ WJB_ELEM
Definition: jsonb.h:25
@ WJB_BEGIN_OBJECT
Definition: jsonb.h:28
@ WJB_BEGIN_ARRAY
Definition: jsonb.h:26
#define JsonbJsonpathExistsStrategyNumber
Definition: jsonb.h:37
#define JB_ROOT_COUNT(jbp_)
Definition: jsonb.h:221
static JsonPathGinNode * extract_jsp_bool_expr(JsonPathGinContext *cxt, JsonPathGinPath path, JsonPathItem *jsp, bool not)
Definition: jsonb_gin.c:584
JsonPathGinNodeType
Definition: jsonb_gin.c:88
@ JSP_GIN_AND
Definition: jsonb_gin.c:90
@ JSP_GIN_ENTRY
Definition: jsonb_gin.c:91
@ JSP_GIN_OR
Definition: jsonb_gin.c:89
static JsonPathGinNode * make_jsp_entry_node_scalar(JsonbValue *scalar, bool iskey)
Definition: jsonb_gin.c:365
Datum gin_compare_jsonb(PG_FUNCTION_ARGS)
Definition: jsonb_gin.c:204
static Datum make_text_key(char flag, const char *str, int len)
Definition: jsonb_gin.c:1323
static List * jsonb_path_ops__extract_nodes(JsonPathGinContext *cxt, JsonPathGinPath path, JsonbValue *scalar, List *nodes)
Definition: jsonb_gin.c:479
static GinTernaryValue execute_jsp_gin_node(JsonPathGinNode *node, void *check, bool ternary)
Definition: jsonb_gin.c:800
static JsonPathGinNode * make_jsp_expr_node_args(JsonPathGinNodeType type, List *args)
Definition: jsonb_gin.c:383
static JsonPathGinNode * extract_jsp_path_expr(JsonPathGinContext *cxt, JsonPathGinPath path, JsonPathItem *jsp, JsonbValue *scalar)
Definition: jsonb_gin.c:565
bool(* JsonPathGinAddPathItemFunc)(JsonPathGinPath *path, JsonPathItem *jsp)
Definition: jsonb_gin.c:134
static bool jsonb_ops__add_path_item(JsonPathGinPath *path, JsonPathItem *jsp)
Definition: jsonb_gin.c:279
static JsonPathGinNode * make_jsp_expr_node(JsonPathGinNodeType type, int nargs)
Definition: jsonb_gin.c:371
static Datum make_scalar_key(const JsonbValue *scalarVal, bool is_key)
Definition: jsonb_gin.c:1361
static List * jsonb_ops__extract_nodes(JsonPathGinContext *cxt, JsonPathGinPath path, JsonbValue *scalar, List *nodes)
Definition: jsonb_gin.c:409
static Datum * extract_jsp_query(JsonPath *jp, StrategyNumber strat, bool pathOps, int32 *nentries, Pointer **extra_data)
Definition: jsonb_gin.c:749
Datum gin_consistent_jsonb_path(PG_FUNCTION_ARGS)
Definition: jsonb_gin.c:1219
Datum gin_extract_jsonb_query(PG_FUNCTION_ARGS)
Definition: jsonb_gin.c:849
Datum gin_extract_jsonb(PG_FUNCTION_ARGS)
Definition: jsonb_gin.c:230
static List * extract_jsp_path_expr_nodes(JsonPathGinContext *cxt, JsonPathGinPath path, JsonPathItem *jsp, JsonbValue *scalar)
Definition: jsonb_gin.c:505
static void init_gin_entries(GinEntries *entries, int preallocated)
Definition: jsonb_gin.c:163
struct PathHashStack PathHashStack
struct GinEntries GinEntries
static bool jsonb_path_ops__add_path_item(JsonPathGinPath *path, JsonPathItem *jsp)
Definition: jsonb_gin.c:324
Datum gin_extract_jsonb_query_path(PG_FUNCTION_ARGS)
Definition: jsonb_gin.c:1179
Datum gin_consistent_jsonb(PG_FUNCTION_ARGS)
Definition: jsonb_gin.c:930
Datum gin_extract_jsonb_path(PG_FUNCTION_ARGS)
Definition: jsonb_gin.c:1089
struct JsonPathGinPathItem JsonPathGinPathItem
static int add_gin_entry(GinEntries *entries, Datum entry)
Definition: jsonb_gin.c:172
List *(* JsonPathGinExtractNodesFunc)(JsonPathGinContext *cxt, JsonPathGinPath path, JsonbValue *scalar, List *nodes)
Definition: jsonb_gin.c:141
static JsonPathGinNode * make_jsp_expr_node_binary(JsonPathGinNodeType type, JsonPathGinNode *arg1, JsonPathGinNode *arg2)
Definition: jsonb_gin.c:396
union JsonPathGinPath JsonPathGinPath
Datum gin_triconsistent_jsonb_path(PG_FUNCTION_ARGS)
Definition: jsonb_gin.c:1270
static JsonPathGinNode * make_jsp_entry_node(Datum entry)
Definition: jsonb_gin.c:354
static void emit_jsp_gin_entries(JsonPathGinNode *node, GinEntries *entries)
Definition: jsonb_gin.c:720
Datum gin_triconsistent_jsonb(PG_FUNCTION_ARGS)
Definition: jsonb_gin.c:1013
JsonbIterator * JsonbIteratorInit(JsonbContainer *container)
Definition: jsonb_util.c:935
void JsonbHashScalarValue(const JsonbValue *scalarVal, uint32 *hash)
Definition: jsonb_util.c:1443
JsonbIteratorToken JsonbIteratorNext(JsonbIterator **it, JsonbValue *val, bool skipNested)
Definition: jsonb_util.c:973
void jspGetLeftArg(JsonPathItem *v, JsonPathItem *a)
Definition: jsonpath.c:1166
void jspGetArg(JsonPathItem *v, JsonPathItem *a)
Definition: jsonpath.c:1081
void jspInit(JsonPathItem *v, JsonPath *js)
Definition: jsonpath.c:980
char * jspGetString(JsonPathItem *v, int32 *len)
Definition: jsonpath.c:1226
bool jspGetNext(JsonPathItem *v, JsonPathItem *a)
Definition: jsonpath.c:1099
void jspGetRightArg(JsonPathItem *v, JsonPathItem *a)
Definition: jsonpath.c:1188
#define PG_GETARG_JSONPATH_P(x)
Definition: jsonpath.h:46
#define jspIsScalar(type)
Definition: jsonpath.h:50
JsonPathItemType
Definition: jsonpath.h:63
@ jpiString
Definition: jsonpath.h:65
@ jpiIndexArray
Definition: jsonpath.h:87
@ jpiAny
Definition: jsonpath.h:88
@ jpiBool
Definition: jsonpath.h:67
@ jpiAnyArray
Definition: jsonpath.h:85
@ jpiExists
Definition: jsonpath.h:94
@ jpiNotEqual
Definition: jsonpath.h:73
@ jpiNot
Definition: jsonpath.h:70
@ jpiAnd
Definition: jsonpath.h:68
@ jpiOr
Definition: jsonpath.h:69
@ jpiRoot
Definition: jsonpath.h:91
@ jpiFilter
Definition: jsonpath.h:93
@ jpiNull
Definition: jsonpath.h:64
@ jpiCurrent
Definition: jsonpath.h:90
@ jpiEqual
Definition: jsonpath.h:72
@ jpiKey
Definition: jsonpath.h:89
@ jpiNumeric
Definition: jsonpath.h:66
@ jpiAnyKey
Definition: jsonpath.h:86
#define JSONPATH_LAX
Definition: jsonpath.h:31
List * lappend(List *list, void *datum)
Definition: list.c:339
void pfree(void *pointer)
Definition: mcxt.c:1594
void * palloc(Size size)
Definition: mcxt.c:1365
struct NumericData * Numeric
Definition: numeric.h:57
void * arg
const void size_t len
#define lfirst(lc)
Definition: pg_list.h:172
static int list_length(const List *l)
Definition: pg_list.h:152
#define NIL
Definition: pg_list.h:68
#define linitial(l)
Definition: pg_list.h:178
#define snprintf
Definition: port.h:260
static uint32 DatumGetUInt32(Datum X)
Definition: postgres.h:232
static Datum PointerGetDatum(const void *X)
Definition: postgres.h:332
uint64_t Datum
Definition: postgres.h:70
static Pointer DatumGetPointer(Datum X)
Definition: postgres.h:322
static Datum UInt32GetDatum(uint32 X)
Definition: postgres.h:242
tree ctl root
Definition: radixtree.h:1857
static unsigned hash(unsigned *uv, int n)
Definition: rege_dfa.c:715
void check_stack_depth(void)
Definition: stack_depth.c:95
uint16 StrategyNumber
Definition: stratnum.h:22
int allocated
Definition: jsonb_gin.c:84
Datum * buf
Definition: jsonb_gin.c:82
int count
Definition: jsonb_gin.c:83
JsonPathGinExtractNodesFunc extract_nodes
Definition: jsonb_gin.c:150
JsonPathGinAddPathItemFunc add_path_item
Definition: jsonb_gin.c:149
union JsonPathGinNode::@25 val
JsonPathGinNodeType type
Definition: jsonb_gin.c:99
JsonPathGinNode * args[FLEXIBLE_ARRAY_MEMBER]
Definition: jsonb_gin.c:108
Datum entryDatum
Definition: jsonb_gin.c:105
JsonPathItemType type
Definition: jsonb_gin.c:121
struct JsonPathGinPathItem * parent
Definition: jsonb_gin.c:119
int32 datalen
Definition: jsonpath.h:181
union JsonPathItem::@152 content
JsonPathItemType type
Definition: jsonpath.h:137
char * data
Definition: jsonpath.h:180
struct JsonPathItem::@152::@156 value
uint32 header
Definition: jsonpath.h:26
enum jbvType type
Definition: jsonb.h:257
char * val
Definition: jsonb.h:266
Definition: jsonb.h:215
JsonbContainer root
Definition: jsonb.h:217
Definition: pg_list.h:54
uint32 hash
Definition: jsonb_gin.c:75
struct PathHashStack * parent
Definition: jsonb_gin.c:76
Definition: type.h:96
Definition: c.h:706
char * flag(int b)
Definition: test-ctype.c:33
JsonPathGinPathItem * items
Definition: jsonb_gin.c:127
static Size VARSIZE_ANY_EXHDR(const void *PTR)
Definition: varatt.h:472
static char * VARDATA(const void *PTR)
Definition: varatt.h:305
static char * VARDATA_ANY(const void *PTR)
Definition: varatt.h:486
static void SET_VARSIZE(void *PTR, Size len)
Definition: varatt.h:432
int varstr_cmp(const char *arg1, int len1, const char *arg2, int len2, Oid collid)
Definition: varlena.c:1308
const char * type