/* * Create a textual representation of a JsonbValue that will serve as a GIN * key in a jsonb_ops index. is_key is true if the JsonbValue is a key, * or if it is a string array element (since we pretend those are keys, * see jsonb.h). */ static Datum make_scalar_key(const JsonbValue *scalarVal, bool is_key) { Datum item; char *cstr; switch (scalarVal->type) { case jbvNull: Assert(!is_key); item = make_text_key(JGINFLAG_NULL, "", 0); break; case jbvBool: Assert(!is_key); item = make_text_key(JGINFLAG_BOOL, scalarVal->val.boolean ? "t" : "f", 1); break; case jbvNumeric: Assert(!is_key); /* * A normalized textual representation, free of trailing zeroes, * is required so that numerically equal values will produce equal * strings. * * It isn't ideal that numerics are stored in a relatively bulky * textual format. However, it's a notationally convenient way of * storing a "union" type in the GIN B-Tree, and indexing Jsonb * strings takes precedence. */ cstr = numeric_normalize(scalarVal->val.numeric); item = make_text_key(JGINFLAG_NUM, cstr, strlen(cstr)); pfree(cstr); break; case jbvString: item = make_text_key(is_key ? JGINFLAG_KEY : JGINFLAG_STR, scalarVal->val.string.val, scalarVal->val.string.len); break; default: elog(ERROR, "unrecognized jsonb scalar type: %d", scalarVal->type); item = 0; /* keep compiler quiet */ break; } return item; }
/* * Create a textual representation of a jsonbValue for GIN storage. */ static text * make_scalar_key(const JsonbValue * scalarVal, char flag) { text *item; char *cstr; switch (scalarVal->type) { case jbvNull: item = make_text_key("n", 1, flag); break; case jbvBool: item = make_text_key(scalarVal->val.boolean ? "t" : "f", 1, flag); break; case jbvNumeric: /* * A normalized textual representation, free of trailing zeroes is * is required. * * It isn't ideal that numerics are stored in a relatively bulky * textual format. However, it's a notationally convenient way of * storing a "union" type in the GIN B-Tree, and indexing Jsonb * strings takes precedence. */ cstr = numeric_normalize(scalarVal->val.numeric); item = make_text_key(cstr, strlen(cstr), flag); pfree(cstr); break; case jbvString: item = make_text_key(scalarVal->val.string.val, scalarVal->val.string.len, flag); break; default: elog(ERROR, "invalid jsonb scalar type"); } return item; }
/* Append JsonPathGinPathItem to JsonPathGinPath (jsonb_ops) */ static bool jsonb_ops__add_path_item(JsonPathGinPath *path, JsonPathItem *jsp) { JsonPathGinPathItem *pentry; Datum keyName; switch (jsp->type) { case jpiRoot: path->items = NULL; /* reset path */ return true; case jpiKey: { int len; char *key = jspGetString(jsp, &len); keyName = make_text_key(JGINFLAG_KEY, key, len); break; } case jpiAny: case jpiAnyKey: case jpiAnyArray: case jpiIndexArray: keyName = PointerGetDatum(NULL); break; default: /* other path items like item methods are not supported */ return false; } pentry = palloc(sizeof(*pentry)); pentry->type = jsp->type; pentry->keyName = keyName; pentry->parent = path->items; path->items = pentry; return true; }
Datum gin_extract_jsonb_query(PG_FUNCTION_ARGS) { int32 *nentries = (int32 *) PG_GETARG_POINTER(1); StrategyNumber strategy = PG_GETARG_UINT16(2); int32 *searchMode = (int32 *) PG_GETARG_POINTER(6); Datum *entries; 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; } else if (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)); } else if (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; deconstruct_array(query, TEXTOID, -1, false, 'i', &key_datums, &key_nulls, &key_count); entries = (Datum *) palloc(sizeof(Datum) * key_count); for (i = 0, j = 0; i < key_count; i++) { /* Nulls in the array are ignored */ if (key_nulls[i]) continue; entries[j++] = make_text_key(JGINFLAG_KEY, VARDATA(key_datums[i]), VARSIZE(key_datums[i]) - VARHDRSZ); } *nentries = j; /* ExistsAll with no keys should match everything */ if (j == 0 && strategy == JsonbExistsAllStrategyNumber) *searchMode = GIN_SEARCH_MODE_ALL; } else { elog(ERROR, "unrecognized strategy number: %d", strategy); entries = NULL; /* keep compiler quiet */ } PG_RETURN_POINTER(entries); }