} PathHashStack;
static text *make_text_key(const char *str, int len, char flag);
-static text *make_scalar_text_key(const JsonbValue * v, char flag);
+static text *make_scalar_key(const JsonbValue * v, char flag);
/*
*
/*
* Serialize keys and elements as one. Array elements are indexed as
* keys, for the benefit of JsonbContainsStrategyNumber (i.e. so that
- * the structure of the index comports with the general Jsonb notion of
- * containment).
+ * the structure of the index comports with the general Jsonb
+ * definition of containment).
*
* See remarks above findJsonbValueFromSuperHeader() for information on
* our definition of containment as it relates to elements and
- * key/value pairs. Note also that the recheck flag is set for
- * JsonbContainsStrategyNumber.
+ * key/value pairs. We do not treat "raw scalar" pseudo arrays
+ * specially here, which is consistent with that definition. Note also
+ * that the recheck flag is set for JsonbContainsStrategyNumber.
*/
switch (r)
{
+ /*
+ * FIXME: Figure out a way of making checking the existence of a
+ * key or text element work, without spuriously returning on
+ * non-string elements just because they happen to have the same
+ * textual representation. We can't just avoid storing non-string
+ * elements, because we need those for testing containment.
+ */
case WJB_KEY:
case WJB_ELEM:
- entries[i++] = PointerGetDatum(make_scalar_text_key(&v, KEYELEMFLAG));
+ entries[i++] = PointerGetDatum(make_scalar_key(&v, JKEYELEM));
break;
case WJB_VALUE:
- entries[i++] = PointerGetDatum(make_scalar_text_key(&v, VALFLAG));
+ entries[i++] = PointerGetDatum(make_scalar_key(&v, JVAL));
break;
default:
break;
*nentries = 1;
entries = (Datum *) palloc(sizeof(Datum));
item = make_text_key(VARDATA_ANY(query), VARSIZE_ANY_EXHDR(query),
- KEYELEMFLAG);
+ JKEYELEM);
entries[0] = PointerGetDatum(item);
}
else if (strategy == JsonbExistsAnyStrategyNumber ||
continue;
item = make_text_key(VARDATA(key_datums[i]),
VARSIZE(key_datums[i]) - VARHDRSZ,
- KEYELEMFLAG);
+ JKEYELEM);
entries[j++] = PointerGetDatum(item);
}
bool has_maybe = false;
/*
- * All extracted keys must be present. Combination of GIN_MAYBE and
- * GIN_TRUE gives GIN_MAYBE result because all keys may be present in
- * this situation.
+ * All extracted keys must be present. A combination of GIN_MAYBE and
+ * GIN_TRUE induces a GIN_MAYBE result, because all keys may be present
+ * in this situation.
*/
for (i = 0; i < nkeys; i++)
{
}
/*
* Index doesn't have information about correspondence of Jsonb keys
- * and values (as distinct from GIN keys, which both are stored as). So
- * invariably we recheck. In thiconsistent function we reflect it as
+ * and values (as distinct from GIN keys, which both are stored as), so
+ * invariably we recheck. In the consistent function we reflect it as
* GIN_MAYBE in response to no GIN_MAYBE at input.
*/
if (!has_maybe && res == GIN_TRUE)
* Create a textual representation of a jsonbValue for GIN storage.
*/
static text *
-make_scalar_text_key(const JsonbValue * v, char flag)
+make_scalar_key(const JsonbValue * v, char flag)
{
text *item;
char *cstr;
switch (v->type)
{
case jbvNull:
- item = make_text_key(NULL, 0, NULLFLAG);
+ item = make_text_key(NULL, 0, JNULL);
break;
case jbvBool:
item = make_text_key(v->boolean ? "t" : "f", 1, flag);
JsonbValue kval;
+ /*
+ * We only match Object keys (which are naturally always Strings), or
+ * string elements in arrays. In particular, we do not match non-string
+ * scalar elements.
+ */
kval.type = jbvString;
kval.string.val = VARDATA_ANY(key);
kval.string.len = VARSIZE_ANY_EXHDR(key);
* keys (or only values), but for now it's always keys and values together, or
* just array elements.
*/
-#define KEYELEMFLAG 'K'
-#define VALFLAG 'V'
-#define NULLFLAG 'N'
+#define JKEYELEM 'K'
+#define JVAL 'V'
+#define JNULL 'N'
#define JsonbContainsStrategyNumber 7
#define JsonbExistsStrategyNumber 9
{"world":"CAB", "org":21, "indexed":true, "line":988, "abstract":"ABC"}
{"title":"CBC", "status":66, "line":989}
{}
+{"array":[5]}
{"array":["foo", "bar", "baz"]}
{"array":["bar", "baz", "foo"]}
{"array":["bar", "baz"]}
3
(1 row)
+-- type sensitive array exists - should return no rows (since "exists" only
+-- matches strings that are either object keys or array elements)
+SELECT count(*) from testjsonb WHERE j->'array' ? '5'::text;
+ count
+-------
+ 0
+(1 row)
+
+-- However, a raw scalar is *contained* within the array
+SELECT count(*) from testjsonb WHERE j->'array' @> '5'::jsonb;
+ count
+-------
+ 1
+(1 row)
+
SELECT jsonb_exists_any('{"a":null, "b":"qq"}', ARRAY['a','b']);
jsonb_exists_any
------------------
SELECT count(*) FROM testjsonb WHERE j @> '{}';
count
-------
- 1008
+ 1009
(1 row)
SELECT count(*) FROM testjsonb WHERE j ? 'public';
3
(1 row)
+-- type sensitive array exists - should return no rows (since "exists" only
+-- matches strings that are either object keys or array elements)
+SELECT count(*) from testjsonb WHERE j->'array' ? '5'::text;
+ count
+-------
+ 0
+(1 row)
+
+-- However, a raw scalar is *contained* within the array
+SELECT count(*) from testjsonb WHERE j->'array' @> '5'::jsonb;
+ count
+-------
+ 1
+(1 row)
+
RESET enable_seqscan;
SELECT count(*) FROM (SELECT (jsonb_each(j)).key FROM testjsonb) AS wow;
count
-------
- 4787
+ 4788
(1 row)
SELECT key, count(*) FROM (SELECT (jsonb_each(j)).key FROM testjsonb) AS wow GROUP BY key ORDER BY count DESC, key;
subtitle | 169
auth | 168
abstract | 161
- array | 4
+ array | 5
age | 2
(24 rows)
SELECT count(distinct j) FROM testjsonb;
count
-------
- 890
+ 891
(1 row)
SET enable_hashagg = off;
SELECT count(*) FROM (SELECT j FROM (SELECT * FROM testjsonb UNION ALL SELECT * FROM testjsonb) js GROUP BY j) js2;
count
-------
- 890
+ 891
(1 row)
SET enable_hashagg = on;
SELECT count(*) FROM (SELECT j FROM (SELECT * FROM testjsonb UNION ALL SELECT * FROM testjsonb) js GROUP BY j) js2;
count
-------
- 890
+ 891
(1 row)
SELECT distinct * FROM (values (jsonb '{}' || ''),('{}')) v(j);
SELECT count(*) FROM testjsonb WHERE j @> '{}';
count
-------
- 1008
+ 1009
(1 row)
RESET enable_seqscan;
3
(1 row)
+-- type sensitive array exists - should return no rows (since "exists" only
+-- matches strings that are either object keys or array elements)
+SELECT count(*) from testjsonb WHERE j->'array' ? '5'::text;
+ count
+-------
+ 0
+(1 row)
+
+-- However, a raw scalar is *contained* within the array
+SELECT count(*) from testjsonb WHERE j->'array' @> '5'::jsonb;
+ count
+-------
+ 1
+(1 row)
+
SELECT jsonb_exists_any('{"a":null, "b":"qq"}', ARRAY['a','b']);
jsonb_exists_any
------------------
SELECT count(*) FROM testjsonb WHERE j @> '{}';
count
-------
- 1008
+ 1009
(1 row)
SELECT count(*) FROM testjsonb WHERE j ? 'public';
3
(1 row)
+-- type sensitive array exists - should return no rows (since "exists" only
+-- matches strings that are either object keys or array elements)
+SELECT count(*) from testjsonb WHERE j->'array' ? '5'::text;
+ count
+-------
+ 0
+(1 row)
+
+-- However, a raw scalar is *contained* within the array
+SELECT count(*) from testjsonb WHERE j->'array' @> '5'::jsonb;
+ count
+-------
+ 1
+(1 row)
+
RESET enable_seqscan;
SELECT count(*) FROM (SELECT (jsonb_each(j)).key FROM testjsonb) AS wow;
count
-------
- 4787
+ 4788
(1 row)
SELECT key, count(*) FROM (SELECT (jsonb_each(j)).key FROM testjsonb) AS wow GROUP BY key ORDER BY count DESC, key;
subtitle | 169
auth | 168
abstract | 161
- array | 4
+ array | 5
age | 2
(24 rows)
SELECT count(distinct j) FROM testjsonb;
count
-------
- 890
+ 891
(1 row)
SET enable_hashagg = off;
SELECT count(*) FROM (SELECT j FROM (SELECT * FROM testjsonb UNION ALL SELECT * FROM testjsonb) js GROUP BY j) js2;
count
-------
- 890
+ 891
(1 row)
SET enable_hashagg = on;
SELECT count(*) FROM (SELECT j FROM (SELECT * FROM testjsonb UNION ALL SELECT * FROM testjsonb) js GROUP BY j) js2;
count
-------
- 890
+ 891
(1 row)
SELECT distinct * FROM (values (jsonb '{}' || ''),('{}')) v(j);
SELECT count(*) FROM testjsonb WHERE j @> '{}';
count
-------
- 1008
+ 1009
(1 row)
RESET enable_seqscan;
SELECT jsonb '{"a":"null", "b":"qq"}' ? 'a';
-- array exists - array elements should behave as keys
SELECT count(*) from testjsonb WHERE j->'array' ? 'bar';
+-- type sensitive array exists - should return no rows (since "exists" only
+-- matches strings that are either object keys or array elements)
+SELECT count(*) from testjsonb WHERE j->'array' ? '5'::text;
+-- However, a raw scalar is *contained* within the array
+SELECT count(*) from testjsonb WHERE j->'array' @> '5'::jsonb;
SELECT jsonb_exists_any('{"a":null, "b":"qq"}', ARRAY['a','b']);
SELECT jsonb_exists_any('{"a":null, "b":"qq"}', ARRAY['b','a']);
-- array exists - array elements should behave as keys (for GIN index scans too)
CREATE INDEX jidx_array ON testjsonb USING gin((j->'array'));
SELECT count(*) from testjsonb WHERE j->'array' ? 'bar';
+-- type sensitive array exists - should return no rows (since "exists" only
+-- matches strings that are either object keys or array elements)
+SELECT count(*) from testjsonb WHERE j->'array' ? '5'::text;
+-- However, a raw scalar is *contained* within the array
+SELECT count(*) from testjsonb WHERE j->'array' @> '5'::jsonb;
RESET enable_seqscan;