/* Node in jsonpath expression tree */ struct JsonPathGinNode
{
JsonPathGinNodeType type; union
{ int nargs; /* valid for OR and AND nodes */ int entryIndex; /* index in GinEntries array, valid for ENTRY
* nodes after entries output */
Datum entryDatum; /* path hash or key name/scalar, valid for
* ENTRY nodes before entries output */
} val;
JsonPathGinNode *args[FLEXIBLE_ARRAY_MEMBER]; /* valid for OR and AND
* nodes */
};
/* *jsonb_opsentryextractedfromjsonpathitem.Correspondingpathitem *maybe:'.key','.*','.**','[index]'or'[*]'. *Entrytypeisstoredin'type'field.
*/ typedefstruct JsonPathGinPathItem
{ struct JsonPathGinPathItem *parent;
Datum keyName; /* key name (for '.key' path item) or NULL */
JsonPathItemType type; /* type of jsonpath item */
} JsonPathGinPathItem;
/* GIN representation of the extracted json path */ typedefunion JsonPathGinPath
{
JsonPathGinPathItem *items; /* list of path items (jsonb_ops) */
uint32 hash; /* hash of the path (jsonb_path_ops) */
} JsonPathGinPath;
/* Callback, which stores information about path item into JsonPathGinPath */ typedefbool (*JsonPathGinAddPathItemFunc) (JsonPathGinPath *path,
JsonPathItem *jsp);
/* *Callback,whichextractssetofnodesfromstatementof1stkind *(scalar!=NULL)orstatementof2ndkind(scalar==NULL).
*/ typedef List *(*JsonPathGinExtractNodesFunc) (JsonPathGinContext *cxt,
JsonPathGinPath path,
JsonbValue *scalar,
List *nodes);
Datum
gin_compare_jsonb(PG_FUNCTION_ARGS)
{
text *arg1 = PG_GETARG_TEXT_PP(0);
text *arg2 = PG_GETARG_TEXT_PP(1);
int32 result; char *a1p,
*a2p; int len1,
len2;
/* Append a list of nodes from the jsonpath (jsonb_ops). */ static List *
jsonb_ops__extract_nodes(JsonPathGinContext *cxt, JsonPathGinPath path,
JsonbValue *scalar, List *nodes)
{
JsonPathGinPathItem *pentry;
if (scalar)
{
JsonPathGinNode *node;
/* *Appendpathentrynodesonlyifscalarisprovided.Seeheader *commentfordetails.
*/ for (pentry = path.items; pentry; pentry = pentry->parent)
{ if (pentry->type == jpiKey) /* only keys are indexed */
nodes = lappend(nodes, make_jsp_entry_node(pentry->keyName));
}
/* Append scalar node for equality queries. */ if (scalar->type == jbvString)
{
JsonPathGinPathItem *last = path.items;
GinTernaryValue key_entry;
/* Append a list of nodes from the jsonpath (jsonb_path_ops). */ static List *
jsonb_path_ops__extract_nodes(JsonPathGinContext *cxt, JsonPathGinPath path,
JsonbValue *scalar, List *nodes)
{ if (scalar)
{ /* append path hash node for equality queries */
uint32 hash = path.hash;
if (jspIsScalar(left_item.type))
{
scalar_item = &left_item;
path_item = &right_item;
} elseif (jspIsScalar(right_item.type))
{
scalar_item = &right_item;
path_item = &left_item;
} else return NULL; /* at least one operand should be a scalar */
default: return NULL; /* not a boolean expression */
}
}
/* Recursively emit all GIN entries found in the node tree */ staticvoid
emit_jsp_gin_entries(JsonPathGinNode *node, GinEntries *entries)
{
check_stack_depth();
switch (node->type)
{ case JSP_GIN_ENTRY: /* replace datum with its index in the array */
node->val.entryIndex = add_gin_entry(entries, node->val.entryDatum); break;
case JSP_GIN_OR: case JSP_GIN_AND:
{ int i;
for (i = 0; i < node->val.nargs; i++)
emit_jsp_gin_entries(node->args[i], entries);
switch (node->type)
{ case JSP_GIN_AND:
res = GIN_TRUE; for (i = 0; i < node->val.nargs; i++)
{
v = execute_jsp_gin_node(node->args[i], check, ternary); if (v == GIN_FALSE) return GIN_FALSE; elseif (v == GIN_MAYBE)
res = GIN_MAYBE;
} return res;
case JSP_GIN_OR:
res = GIN_FALSE; for (i = 0; i < node->val.nargs; i++)
{
v = execute_jsp_gin_node(node->args[i], check, ternary); if (v == GIN_TRUE) return GIN_TRUE; elseif (v == GIN_MAYBE)
res = GIN_MAYBE;
} return res;
case JSP_GIN_ENTRY:
{ int index = node->val.entryIndex;
if (strategy == JsonbContainsStrategyNumber)
{ /* Query is a jsonb, so just apply gin_extract_jsonb... */
entries = (Datum *)
DatumGetPointer(DirectFunctionCall2(gin_extract_jsonb,
PG_GETARG_DATUM(0),
PointerGetDatum(nentries))); /* ...although "contains {}" requires a full index scan */ if (*nentries == 0)
*searchMode = GIN_SEARCH_MODE_ALL;
} elseif (strategy == JsonbExistsStrategyNumber)
{ /* Query is a text string, which we treat as a key */
text *query = PG_GETARG_TEXT_PP(0);
*nentries = 1;
entries = (Datum *) palloc(sizeof(Datum));
entries[0] = make_text_key(JGINFLAG_KEY,
VARDATA_ANY(query),
VARSIZE_ANY_EXHDR(query));
} elseif (strategy == JsonbExistsAnyStrategyNumber ||
strategy == JsonbExistsAllStrategyNumber)
{ /* Query is a text array; each element is treated as a key */
ArrayType *query = PG_GETARG_ARRAYTYPE_P(0);
Datum *key_datums; bool *key_nulls; int key_count; int i,
j;
for (i = 0, j = 0; i < key_count; i++)
{ /* Nulls in the array are ignored */ if (key_nulls[i]) continue; /* We rely on the array elements not being toasted */
entries[j++] = make_text_key(JGINFLAG_KEY,
VARDATA_ANY(key_datums[i]),
VARSIZE_ANY_EXHDR(key_datums[i]));
}
*nentries = j; /* ExistsAll with no keys should match everything */ if (j == 0 && strategy == JsonbExistsAllStrategyNumber)
*searchMode = GIN_SEARCH_MODE_ALL;
} elseif (strategy == JsonbJsonpathPredicateStrategyNumber ||
strategy == JsonbJsonpathExistsStrategyNumber)
{
JsonPath *jp = PG_GETARG_JSONPATH_P(0);
Pointer **extra_data = (Pointer **) PG_GETARG_POINTER(4);
if (strategy == JsonbContainsStrategyNumber)
{ /* *Wemustalwaysrecheck,sincewecan'ttellfromtheindexwhether *thepositionsofthematcheditemsmatchthestructureofthequery *object.(Evenifwecould,we'dalsohavetoworryabouthashed *keysandtheindex'sfailuretodistinguishkeysfromstringarray *elements.)However,thetuplecertainlydoesn'tmatchunlessit *containsallthequerykeys.
*/
*recheck = true; for (i = 0; i < nkeys; i++)
{ if (!check[i])
{
res = false; break;
}
}
} elseif (strategy == JsonbExistsStrategyNumber)
{ /* *Althoughthekeyiscertainlypresentintheindex,wemustrecheck *because(1)thekeymightbehashed,and(2)theindexmatchmight *beforakeythat'snotattopleveloftheJSONobject.For(1), *wecouldlookatthequerykeytoseeifit'shashedandnot *recheckifnot,buttheindexlacksenoughinfototellabout(2).
*/
*recheck = true;
res = true;
} elseif (strategy == JsonbExistsAnyStrategyNumber)
{ /* As for plain exists, we must recheck */
*recheck = true;
res = true;
} elseif (strategy == JsonbExistsAllStrategyNumber)
{ /* As for plain exists, we must recheck */
*recheck = true; /* ... but unless all the keys are present, we can say "false" */ for (i = 0; i < nkeys; i++)
{ if (!check[i])
{
res = false; break;
}
}
} elseif (strategy == JsonbJsonpathPredicateStrategyNumber ||
strategy == JsonbJsonpathExistsStrategyNumber)
{
*recheck = true;
switch (r)
{ case WJB_BEGIN_ARRAY: case WJB_BEGIN_OBJECT: /* Push a stack level for this object */
parent = stack;
stack = (PathHashStack *) palloc(sizeof(PathHashStack));
/* *Wepassforwardhashesfromouternestinglevelssothat *thehashesfornestedvalueswillincludeouterkeysas *wellastheirownkeys. * *Nestinganarraywithinanotherarraywillnotalter *innermostscalarelementhashvalues,butthatseems *inconsequential.
*/
stack->hash = parent->hash;
stack->parent = parent; break; case WJB_KEY: /* mix this key into the current outer hash */
JsonbHashScalarValue(&v, &stack->hash); /* hash is now ready to incorporate the value */ break; case WJB_ELEM: case WJB_VALUE: /* mix the element or value's hash into the prepared hash */
JsonbHashScalarValue(&v, &stack->hash); /* and emit an index entry */
add_gin_entry(&entries, UInt32GetDatum(stack->hash)); /* reset hash for next key, value, or sub-object */
stack->hash = stack->parent->hash; break; case WJB_END_ARRAY: case WJB_END_OBJECT: /* Pop the stack */
parent = stack->parent;
pfree(stack);
stack = parent; /* reset hash for next key, value, or sub-object */ if (stack->parent)
stack->hash = stack->parent->hash; else
stack->hash = 0; break; default:
elog(ERROR, "invalid JsonbIteratorNext rc: %d", (int) r);
}
}
if (strategy == JsonbContainsStrategyNumber)
{ /* Query is a jsonb, so just apply gin_extract_jsonb_path ... */
entries = (Datum *)
DatumGetPointer(DirectFunctionCall2(gin_extract_jsonb_path,
PG_GETARG_DATUM(0),
PointerGetDatum(nentries)));
/* ... although "contains {}" requires a full index scan */ if (*nentries == 0)
*searchMode = GIN_SEARCH_MODE_ALL;
} elseif (strategy == JsonbJsonpathPredicateStrategyNumber ||
strategy == JsonbJsonpathExistsStrategyNumber)
{
JsonPath *jp = PG_GETARG_JSONPATH_P(0);
Pointer **extra_data = (Pointer **) PG_GETARG_POINTER(4);