struct PathHashStack *next;
} PathHashStack;
-static text *makeitem(const char *str, int len, char flag);
-static text *makeitemFromValue(const JsonbValue * v, char flag);
-static void hash_stack_value(const JsonbValue * v, PathHashStack * stack);
+static text *make_text_key(const char *str, int len, char flag);
+static text *make_primitive_text_key(const JsonbValue * v, char flag);
+static void hash_primitive_value(const JsonbValue * v, PathHashStack * stack);
/*
*
* benefit of JsonbContainsStrategyNumber.
*/
case WJB_ELEM:
- entries[i++] = PointerGetDatum(makeitemFromValue(&v, KEYELEMFLAG));
+ entries[i++] = PointerGetDatum(make_primitive_text_key(&v, KEYELEMFLAG));
break;
case WJB_VALUE:
- entries[i++] = PointerGetDatum(makeitemFromValue(&v, VALFLAG));
+ entries[i++] = PointerGetDatum(make_primitive_text_key(&v, VALFLAG));
break;
default:
break;
*nentries = 1;
entries = (Datum *) palloc(sizeof(Datum));
- item = makeitem(VARDATA_ANY(query), VARSIZE_ANY_EXHDR(query),
+ item = make_text_key(VARDATA_ANY(query), VARSIZE_ANY_EXHDR(query),
KEYELEMFLAG);
entries[0] = PointerGetDatum(item);
}
/* Nulls in the array are ignored */
if (key_nulls[i])
continue;
- item = makeitem(VARDATA(key_datums[i]),
+ item = make_text_key(VARDATA(key_datums[i]),
VARSIZE(key_datums[i]) - VARHDRSZ,
KEYELEMFLAG);
entries[j++] = PointerGetDatum(item);
JsonbIterator *it;
JsonbValue v;
PathHashStack tail;
- PathHashStack *stack,
- *tmp;
- pg_crc32 path_crc32;
+ PathHashStack *stack;
if (total == 0)
{
*/
while ((r = JsonbIteratorGet(&it, &v, false)) != 0)
{
+ pg_crc32 path_crc32;
+ PathHashStack *tmp;
+
if (i >= total)
{
total *= 2;
entries = (Datum *) repalloc(entries, sizeof(Datum) * total);
}
+ /*
+ * Keys and values CRC'd as one.
+ *
+ * Note that we don't CRC anything that directly reflects the nesting
+ * structure (e.g. whether a structure is an array or object). It's
+ * generally assumed that per column jsonb values frequently have a
+ * somewhat homogeneous structure.
+ */
switch (r)
{
case WJB_BEGIN_ARRAY:
- tmp = stack;
- stack = (PathHashStack *) palloc(sizeof(PathHashStack));
- stack->next = tmp;
- stack->hash_state = tmp->hash_state;
- COMP_CRC32(stack->hash_state, PATH_SEPARATOR, 1);
- break;
case WJB_BEGIN_OBJECT:
/* Preserve stack item for key */
tmp = stack;
case WJB_KEY:
/* Calc hash of key and separated into preserved stack item */
stack->hash_state = stack->next->hash_state;
- hash_stack_value(&v, stack);
+ hash_primitive_value(&v, stack);
COMP_CRC32(stack->hash_state, PATH_SEPARATOR, 1);
break;
case WJB_VALUE:
case WJB_ELEM:
- hash_stack_value(&v, stack);
+ stack->hash_state = stack->next->hash_state;
+ COMP_CRC32(stack->hash_state, PATH_SEPARATOR, 1);
+ hash_primitive_value(&v, stack);
path_crc32 = stack->hash_state;
FIN_CRC32(path_crc32);
entries[i++] = path_crc32;
break;
case WJB_END_ARRAY:
case WJB_END_OBJECT:
- /* Pop stack item */
+ /* Pop the stack */
tmp = stack->next;
pfree(stack);
stack = tmp;
* Build an indexable text value from a cstring and flag
*/
static text *
-makeitem(const char *str, int len, char flag)
+make_text_key(const char *str, int len, char flag)
{
text *item;
* Create a textual representation of a jsonbValue for GIN storage.
*/
static text *
-makeitemFromValue(const JsonbValue * v, char flag)
+make_primitive_text_key(const JsonbValue * v, char flag)
{
text *item;
char *cstr;
switch (v->type)
{
case jbvNull:
- item = makeitem(NULL, 0, NULLFLAG);
+ item = make_text_key(NULL, 0, NULLFLAG);
break;
case jbvBool:
- item = makeitem((v->boolean) ? " t" : " f", 2, flag);
+ item = make_text_key((v->boolean) ? " t" : " f", 2, flag);
break;
case jbvNumeric:
/*
* is required.
*/
cstr = numeric_normalize(v->numeric);
- item = makeitem(cstr, strlen(cstr), flag);
+ item = make_text_key(cstr, strlen(cstr), flag);
pfree(cstr);
break;
case jbvString:
- item = makeitem(v->string.val, v->string.len, flag);
+ item = make_text_key(v->string.val, v->string.len, flag);
break;
default:
- elog(ERROR, "invalid jsonb scalar type");
+ elog(ERROR, "invalid jsonb primitive type: %d", v->type);
}
return item;
}
/*
- * Hash a JsonbValue, and push it on to our stack
+ * Hash a JsonbValue primitive value, and push it on to hashing stack
*/
static void
-hash_stack_value(const JsonbValue * v, PathHashStack * stack)
+hash_primitive_value(const JsonbValue * v, PathHashStack * stack)
{
switch (v->type)
{
COMP_CRC32(stack->hash_state, v->string.val, v->string.len);
break;
default:
- elog(ERROR, "invalid jsonb scalar type");
+ elog(ERROR, "invalid jsonb primitive type");
break;
}
}
(1 row)
-- array exists - array elements should behave as keys
-SELECT 1 from testjsonb WHERE j->'array' ? 'bar';
- ?column?
-----------
- 1
+SELECT count(*) from testjsonb WHERE j->'array' ? 'bar';
+ count
+-------
+ 3
(1 row)
SELECT jsonb_exists_any('{"a":null, "b":"qq"}', ARRAY['a','b']);
-- array exists - array elements should behave as keys (for GiST index scans too)
CREATE INDEX jidx_array ON testjsonb USING gist((j->'array'));
-SELECT 1 from testjsonb WHERE j->'array' ? 'bar';
- ?column?
-----------
- 1
+SELECT count(*) from testjsonb WHERE j->'array' ? 'bar';
+ count
+-------
+ 3
(1 row)
RESET enable_seqscan;
2
(1 row)
+SELECT count(*) FROM testjsonb WHERE j @> '{"array":["foo"]}';
+ count
+-------
+ 3
+(1 row)
+
+SELECT count(*) FROM testjsonb WHERE j @> '{"array":["bar"]}';
+ count
+-------
+ 3
+(1 row)
+
SELECT count(*) FROM testjsonb WHERE j ? 'public';
count
-------
-- array exists - array elements should behave as keys (for GIN index scans too)
CREATE INDEX jidx_array ON testjsonb USING gin((j->'array'));
-SELECT 1 from testjsonb WHERE j->'array' ? 'bar';
- ?column?
-----------
- 1
+SELECT count(*) from testjsonb WHERE j->'array' ? 'bar';
+ count
+-------
+ 3
(1 row)
RESET enable_seqscan;
SELECT count(*) FROM (SELECT (jsonb_each(j)).key FROM testjsonb) AS wow;
count
-------
- 4784
+ 4787
(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
age | 2
- array | 1
(24 rows)
-- sort/hash
SELECT count(distinct j) FROM testjsonb;
count
-------
- 887
+ 890
(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
-------
- 887
+ 890
(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
-------
- 887
+ 890
(1 row)
SELECT distinct * FROM (values (jsonb '{}' || ''),('{}')) v(j);
SELECT jsonb '{"a":null, "b":"qq"}' ? 'c';
SELECT jsonb '{"a":"null", "b":"qq"}' ? 'a';
-- array exists - array elements should behave as keys
-SELECT 1 from testjsonb WHERE j->'array' ? 'bar';
+SELECT count(*) from testjsonb WHERE j->'array' ? 'bar';
SELECT jsonb_exists_any('{"a":null, "b":"qq"}', ARRAY['a','b']);
SELECT jsonb_exists_any('{"a":null, "b":"qq"}', ARRAY['b','a']);
SELECT count(*) FROM testjsonb WHERE j ?& ARRAY['public','disabled'];
-- array exists - array elements should behave as keys (for GiST index scans too)
CREATE INDEX jidx_array ON testjsonb USING gist((j->'array'));
-SELECT 1 from testjsonb WHERE j->'array' ? 'bar';
+SELECT count(*) from testjsonb WHERE j->'array' ? 'bar';
RESET enable_seqscan;
SELECT count(*) FROM testjsonb WHERE j @> '{"wait":"CC", "public":true}';
SELECT count(*) FROM testjsonb WHERE j @> '{"age":25}';
SELECT count(*) FROM testjsonb WHERE j @> '{"age":25.0}';
+SELECT count(*) FROM testjsonb WHERE j @> '{"array":["foo"]}';
+SELECT count(*) FROM testjsonb WHERE j @> '{"array":["bar"]}';
SELECT count(*) FROM testjsonb WHERE j ? 'public';
SELECT count(*) FROM testjsonb WHERE j ?| ARRAY['public','disabled'];
SELECT count(*) FROM testjsonb WHERE j ?& ARRAY['public','disabled'];
-- array exists - array elements should behave as keys (for GIN index scans too)
CREATE INDEX jidx_array ON testjsonb USING gin((j->'array'));
-SELECT 1 from testjsonb WHERE j->'array' ? 'bar';
+SELECT count(*) from testjsonb WHERE j->'array' ? 'bar';
RESET enable_seqscan;