LCOV - differential code coverage report
Current view: top level - src/backend/utils/adt - jsonb_gin.c (source / functions) Coverage Total Hit LBC UIC UBC GBC GIC GNC CBC EUB ECB DCB
Current: Differential Code Coverage HEAD vs 15 Lines: 81.4 % 515 419 4 39 53 4 75 2 338 39 74 3
Current Date: 2023-04-08 17:13:01 Functions: 92.9 % 28 26 2 6 2 18 2 6
Baseline: 15 Line coverage date bins:
Baseline Date: 2023-04-08 15:09:40 (60,120] days: 100.0 % 2 2 2 1
Legend: Lines: hit not hit (180,240] days: 100.0 % 1 1 1
(240..) days: 81.2 % 512 416 4 39 53 4 73 1 338 39 73
Function coverage date bins:
(240..) days: 72.2 % 36 26 2 6 2 18 2 6

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

Generated by: LCOV version v1.16-55-g56c0a2a