Fix from Alexander for jsonb_hash_ops @> issue
authorPeter Geoghegan <[email protected]>
Tue, 11 Mar 2014 01:21:49 +0000 (18:21 -0700)
committerPeter Geoghegan <[email protected]>
Tue, 11 Mar 2014 01:23:16 +0000 (18:23 -0700)
src/backend/utils/adt/jsonb_gin.c
src/test/regress/data/jsonb.data
src/test/regress/expected/jsonb.out
src/test/regress/sql/jsonb.sql

index f9ab4ce97942e110cb80d0f5cc3e100885cc164f..293930d79a3b26e02dd035f6ffb7fa06008e98bd 100644 (file)
@@ -27,9 +27,9 @@ typedef struct PathHashStack
    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);
 
 /*
  *
@@ -75,10 +75,10 @@ gin_extract_jsonb(PG_FUNCTION_ARGS)
                 * 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;
@@ -116,7 +116,7 @@ gin_extract_jsonb_query(PG_FUNCTION_ARGS)
 
        *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);
    }
@@ -142,7 +142,7 @@ gin_extract_jsonb_query(PG_FUNCTION_ARGS)
            /* 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);
@@ -280,9 +280,7 @@ gin_extract_jsonb_hash(PG_FUNCTION_ARGS)
    JsonbIterator *it;
    JsonbValue  v;
    PathHashStack tail;
-   PathHashStack *stack,
-              *tmp;
-   pg_crc32    path_crc32;
+   PathHashStack *stack;
 
    if (total == 0)
    {
@@ -306,21 +304,26 @@ gin_extract_jsonb_hash(PG_FUNCTION_ARGS)
     */
    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;
@@ -330,19 +333,21 @@ gin_extract_jsonb_hash(PG_FUNCTION_ARGS)
            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;
@@ -389,7 +394,7 @@ gin_extract_jsonb_query_hash(PG_FUNCTION_ARGS)
  * 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;
 
@@ -408,7 +413,7 @@ makeitem(const char *str, int len, char flag)
  * 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;
@@ -416,10 +421,10 @@ makeitemFromValue(const JsonbValue * v, char flag)
    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:
            /*
@@ -427,24 +432,24 @@ makeitemFromValue(const JsonbValue * v, char flag)
             * 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)
    {
@@ -465,7 +470,7 @@ hash_stack_value(const JsonbValue * v, PathHashStack * stack)
            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;
    }
 }
index 0672333f644e385bddd67f907eef5e233f1e0a55..90bdc6cf6b78933ef3c01fb5b591de8d8ae9a7db 100644 (file)
 {"title":"CBC", "status":66, "line":989}
 {}
 {"array":["foo", "bar", "baz"]}
+{"array":["bar", "baz", "foo"]}
+{"array":["bar", "baz"]}
+{"array":["baz", "foo"]}
 {"line":991, "abstract":"BA", "node":"BBB"}
 {"line":992, "disabled":true, "pos":29, "public":false}
 {"state":53, "wait":"CB", "subtitle":"CCC", "line":993, "date":"CAC", "public":false, "coauthors":"BB"}
index e7a28f3400ca8f0a1bb44bb6a75545047696c349..cc6dc1a96a3919eab911d5d129493bf7e5025a83 100644 (file)
@@ -740,10 +740,10 @@ SELECT jsonb '{"a":"null", "b":"qq"}' ? 'a';
 (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']);
@@ -1433,10 +1433,10 @@ 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';
- ?column? 
-----------
-        1
+SELECT count(*) from testjsonb  WHERE j->'array' ? 'bar';
+ count 
+-------
+     3
 (1 row)
 
 RESET enable_seqscan;
@@ -1474,6 +1474,18 @@ SELECT count(*) FROM testjsonb WHERE j @> '{"age":25.0}';
      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 
 -------
@@ -1494,17 +1506,17 @@ 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';
- ?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;
@@ -1532,22 +1544,22 @@ SELECT key, count(*) FROM (SELECT (jsonb_each(j)).key FROM testjsonb) AS wow GRO
  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;
@@ -1555,7 +1567,7 @@ SET enable_sort = 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)
 
 SELECT distinct * FROM (values (jsonb '{}' || ''),('{}')) v(j);
index 384bede7656bdabf7a0497aa3f62ff85c3cb8c5b..d546862b8e40f66899d924bde8d85685b4e58c75 100644 (file)
@@ -169,7 +169,7 @@ SELECT jsonb '{"a":null, "b":"qq"}' ? 'b';
 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']);
@@ -321,7 +321,7 @@ 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 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;
 
@@ -335,13 +335,15 @@ SELECT count(*) FROM testjsonb WHERE j @> '{"wait":"CC"}';
 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;