Tweaks
authorPeter Geoghegan <[email protected]>
Sun, 9 Mar 2014 05:52:56 +0000 (21:52 -0800)
committerPeter Geoghegan <[email protected]>
Sun, 9 Mar 2014 05:52:56 +0000 (21:52 -0800)
Formalize the notion that text index storage treats keys and elements as
equivalent.  lexicalCompareJsonbStringValue requires no "arg".

src/backend/utils/adt/jsonb.c
src/backend/utils/adt/jsonb_gin.c
src/backend/utils/adt/jsonb_gist.c
src/backend/utils/adt/jsonb_op.c
src/backend/utils/adt/jsonb_support.c
src/include/utils/jsonb.h

index 14465a0124e66ea234512ae9b7f680560301dbdd..efa0c6c27fe152a617e84c42dca4e285d9acda3a 100644 (file)
@@ -268,7 +268,9 @@ jsonb_put_escaped_value(StringInfo out, JsonbValue * v)
                appendBinaryStringInfo(out, "false", 5);
            break;
        case jbvNumeric:
-           appendStringInfoString(out, DatumGetCString(DirectFunctionCall1(numeric_out, PointerGetDatum(v->numeric))));
+           appendStringInfoString(out,
+                                  DatumGetCString(DirectFunctionCall1(numeric_out,
+                                                                      PointerGetDatum(v->numeric))));
            break;
        default:
            elog(ERROR, "unknown jsonb scalar type");
index d6ee1ef55baf6d284391d0ce2d2abe7e9a7da091..7a26a6628708ddf6c548de93182d93b2d0469734 100644 (file)
@@ -75,7 +75,7 @@ gin_extract_jsonb(PG_FUNCTION_ARGS)
                 * keys.
                 */
            case WJB_ELEM:
-               entries[i++] = PointerGetDatum(makeitemFromValue(&v, KEYFLAG));
+               entries[i++] = PointerGetDatum(makeitemFromValue(&v, KEYELEMFLAG));
                break;
            case WJB_VALUE:
                entries[i++] = PointerGetDatum(makeitemFromValue(&v, VALFLAG));
@@ -116,7 +116,8 @@ gin_extract_jsonb_query(PG_FUNCTION_ARGS)
 
        *nentries = 1;
        entries = (Datum *) palloc(sizeof(Datum));
-       item = makeitem(VARDATA_ANY(query), VARSIZE_ANY_EXHDR(query), KEYFLAG);
+       item = makeitem(VARDATA_ANY(query), VARSIZE_ANY_EXHDR(query),
+                       KEYELEMFLAG);
        entries[0] = PointerGetDatum(item);
    }
    else if (strategy == JsonbExistsAnyStrategyNumber ||
@@ -142,7 +143,8 @@ gin_extract_jsonb_query(PG_FUNCTION_ARGS)
            if (key_nulls[i])
                continue;
            item = makeitem(VARDATA(key_datums[i]),
-                           VARSIZE(key_datums[i]) - VARHDRSZ, KEYFLAG);
+                           VARSIZE(key_datums[i]) - VARHDRSZ,
+                           KEYELEMFLAG);
            entries[j++] = PointerGetDatum(item);
        }
 
index 0b4def9da6d0c7d901a3c1a32d8b26fb72b85d4b..26356a12d3283a02e720f927289895f4ad469e74 100644 (file)
@@ -172,7 +172,7 @@ gjsonb_consistent(PG_FUNCTION_ARGS)
            text       *query = PG_GETARG_TEXT_PP(1);
            int         crc = crc32_Key(VARDATA_ANY(query), VARSIZE_ANY_EXHDR(query));
 
-           qval = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt, sizeof(*qval));
+           qval = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt, sizeof(int));
            *qval = HASHVAL(crc);
 
            fcinfo->flinfo->fn_extra = qval;
@@ -579,7 +579,7 @@ crc32_JsonbValue(JsonbValue * v, uint32 r)
             * This is necessary because array elements are also keys.
             */
        case WJB_ELEM:
-           flag = KEYFLAG;
+           flag = KEYELEMFLAG;
            break;
        case WJB_VALUE:
            flag = VALFLAG;
@@ -615,7 +615,7 @@ static int
 crc32_Key(char *buf, int sz)
 {
    int         crc;
-   char        flag = KEYFLAG;
+   char        flag = KEYELEMFLAG;
 
    INIT_CRC32(crc);
 
index b600a83326b55444213a8cedd2566e577aaec223..e02909f9e2529e53d06b009c46b0557bb3fb83ef 100644 (file)
@@ -59,14 +59,16 @@ jsonb_exists_any(PG_FUNCTION_ARGS)
        plowbound = &lowbound;
 
    /*
-    * we exploit the fact that the pairs list is already sorted into strictly
+    * We exploit the fact that the pairs list is already sorted into strictly
     * increasing order to narrow the findUncompressedJsonbValue search; each
     * search can start one entry past the previous "found" entry, or at the
     * lower bound of the last search.
     */
    for (i = 0; i < v->array.nelems; i++)
    {
-       if (findUncompressedJsonbValueByValue(VARDATA(jb), JB_FLAG_OBJECT | JB_FLAG_ARRAY, plowbound,
+       if (findUncompressedJsonbValueByValue(VARDATA(jb),
+                                             JB_FLAG_OBJECT | JB_FLAG_ARRAY,
+                                             plowbound,
                                              v->array.elems + i) != NULL)
        {
            res = true;
@@ -249,7 +251,7 @@ jsonb_cmp(PG_FUNCTION_ARGS)
    }
 
    /*
-    * this is a btree support function; this is one of the few places where
+    * This is a btree support function; this is one of the few places where
     * memory needs to be explicitly freed.
     */
    PG_FREE_IF_COPY(jb1, 0);
@@ -303,7 +305,7 @@ jsonb_hash(PG_FUNCTION_ARGS)
                        break;
                    case jbvNumeric:
                        crc ^= DatumGetInt32(DirectFunctionCall1(hash_numeric,
-                                               NumericGetDatum(v.numeric)));
+                                                                NumericGetDatum(v.numeric)));
                        break;
                    default:
                        elog(ERROR, "invalid jsonb iterator type");
@@ -432,7 +434,7 @@ deepContains(JsonbIterator ** it1, JsonbIterator ** it2)
                {
                    uint32      j = 0;
 
-                   av = palloc(sizeof(*av) * nelems);
+                   av = palloc(sizeof(JsonbValue) * nelems);
 
                    for (i = 0; i < nelems; i++)
                    {
@@ -477,6 +479,10 @@ deepContains(JsonbIterator ** it1, JsonbIterator ** it2)
    return res;
 }
 
+/*
+ * Convert a Postgres text array to a JsonbSortedArray, with de-duplicated key
+ * elements.
+ */
 static JsonbValue *
 arrayToJsonbSortedArray(ArrayType *a)
 {
@@ -488,9 +494,9 @@ arrayToJsonbSortedArray(ArrayType *a)
                j;
    bool        hasNonUniq = false;
 
-   deconstruct_array(a,
-                     TEXTOID, -1, false, 'i',
-                     &key_datums, &key_nulls, &key_count);
+   /* Extract data for sorting */
+   deconstruct_array(a, TEXTOID, -1, false, 'i', &key_datums, &key_nulls,
+                     &key_count);
 
    if (key_count == 0)
        return NULL;
@@ -498,17 +504,17 @@ arrayToJsonbSortedArray(ArrayType *a)
    /*
     * A text array uses at least eight bytes per element, so any overflow in
     * "key_count * sizeof(JsonbPair)" is small enough for palloc() to catch.
-    * However, credible improvements to the array format could invalidate
-    * that assumption.  Therefore, use an explicit check rather than relying
-    * on palloc() to complain.
+    * However, credible improvements to the array format could invalidate that
+    * assumption.  Therefore, use an explicit check rather than relying on
+    * palloc() to complain.
     */
    if (key_count > MaxAllocSize / sizeof(JsonbPair))
        ereport(ERROR,
                (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
-             errmsg("number of pairs (%d) exceeds the maximum allowed (%d)",
-                    key_count, (int) (MaxAllocSize / sizeof(JsonbPair)))));
+                errmsg("number of pairs (%d) exceeds the maximum allowed (%zu)",
+                       key_count, MaxAllocSize / sizeof(JsonbPair))));
 
-   v = palloc(sizeof(*v));
+   v = palloc(sizeof(JsonbValue));
    v->type = jbvArray;
    v->array.scalar = false;
    v->array.elems = palloc(sizeof(*v->object.pairs) * key_count);
@@ -525,9 +531,13 @@ arrayToJsonbSortedArray(ArrayType *a)
    }
    v->array.nelems = j;
 
+   /*
+    * Actually sort values, determining if any were equal on the basis of full
+    * binary equality (rather than just having the same string length).
+    */
    if (v->array.nelems > 1)
        qsort_arg(v->array.elems, v->array.nelems, sizeof(*v->array.elems),
-       compareJsonbStringValue /* compareJsonbStringValue */ , &hasNonUniq);
+                 compareJsonbStringValue, &hasNonUniq);
 
    if (hasNonUniq)
    {
@@ -536,8 +546,9 @@ arrayToJsonbSortedArray(ArrayType *a)
 
        while (ptr - v->array.elems < v->array.nelems)
        {
+           /* Avoid copying over binary duplicate */
            if (!(ptr->string.len == res->string.len &&
-            memcmp(ptr->string.val, res->string.val, ptr->string.len) == 0))
+                 memcmp(ptr->string.val, res->string.val, ptr->string.len) == 0))
            {
                res++;
                *res = *ptr;
index 3e9cfa0b8cc7a71bb1979a4a8d4b7034f65b5613..561cd50927599d636ad189bec025254ef197e3e8 100644 (file)
@@ -143,14 +143,16 @@ JsonbValueToJsonb(JsonbValue * v)
 /*
  * Compare two jbvString JsonbValue values.
  *
- * Third argument 'arg', when set, should point to bool value which will be set
- * to true if strings are equal and untouched otherwise.
+ * This is a special qsort_arg() comparator used to
+ *
+ * Third argument 'binaryequal', when set, is deferenced to bool true if
+ * strings has full binary equality.
  */
 int
-compareJsonbStringValue(const void *a, const void *b, void *arg)
+compareJsonbStringValue(const void *a, const void *b, void *binaryequal)
 {
-   const JsonbValue *va = a;
-   const JsonbValue *vb = b;
+   const JsonbValue *va = (const JsonbValue *) a;
+   const JsonbValue *vb = (const JsonbValue *) b;
    int         res;
 
    Assert(va->type == jbvString);
@@ -159,8 +161,8 @@ compareJsonbStringValue(const void *a, const void *b, void *arg)
    if (va->string.len == vb->string.len)
    {
        res = memcmp(va->string.val, vb->string.val, va->string.len);
-       if (res == 0 && arg)
-           *(bool *) arg = true;
+       if (res == 0 && binaryequal)
+           *((bool *) binaryequal) = true;
    }
    else
    {
@@ -171,32 +173,26 @@ compareJsonbStringValue(const void *a, const void *b, void *arg)
 }
 
 /*
- * Standard lexical comparison of jsonb strings
- *
- * not to be used for internal operations.
+ * Standard lexical qsort() comparator of jsonb strings.
  *
+ * Sorts strings lexically, using the default text collation.  Used by B-Tree
+ * operators.
  */
 static int
-lexicalCompareJsonbStringValue(const void *a, const void *b, void *arg)
+lexicalCompareJsonbStringValue(const void *a, const void *b)
 {
-   const JsonbValue *va = a;
-   const JsonbValue *vb = b;
-   int         res;
+   const JsonbValue *va = (const JsonbValue *) a;
+   const JsonbValue *vb = (const JsonbValue *) b;
 
    Assert(va->type == jbvString);
    Assert(vb->type == jbvString);
 
-   res = varstr_cmp(va->string.val, va->string.len, vb->string.val,
-                    vb->string.len, DEFAULT_COLLATION_OID);
-
-   if (arg && res == 0 && va->string.len == vb->string.len)
-       *(bool *) arg = true;
-
-   return res;
+   return varstr_cmp(va->string.val, va->string.len, vb->string.val,
+                     vb->string.len, DEFAULT_COLLATION_OID);
 }
 
 /*
- * Give consistent ordering of JsonbValues
+ * Compare 2 JsonbValues
  */
 int
 compareJsonbValue(JsonbValue * a, JsonbValue * b)
@@ -210,7 +206,7 @@ compareJsonbValue(JsonbValue * a, JsonbValue * b)
            case jbvNull:
                return 0;
            case jbvString:
-               return lexicalCompareJsonbStringValue(a, b, NULL);
+               return lexicalCompareJsonbStringValue(a, b);
            case jbvBool:
                if (a->boolean == b->boolean)
                    return 0;
@@ -243,8 +239,7 @@ compareJsonbValue(JsonbValue * a, JsonbValue * b)
                    for (i = 0; i < a->object.npairs; i++)
                    {
                        if ((r = lexicalCompareJsonbStringValue(&a->object.pairs[i].key,
-                                                    &b->object.pairs[i].key,
-                                                        NULL)) != 0)
+                                                               &b->object.pairs[i].key)) != 0)
                            return r;
                        if ((r = compareJsonbValue(&a->object.pairs[i].value,
                                            &b->object.pairs[i].value)) != 0)
@@ -298,7 +293,7 @@ compareJsonbBinaryValue(char *a, char *b)
                switch (v1.type)
                {
                    case jbvString:
-                       res = lexicalCompareJsonbStringValue(&v1, &v2, NULL);
+                       res = lexicalCompareJsonbStringValue(&v1, &v2);
                        break;
                    case jbvBool:
                        if (v1.boolean == v2.boolean)
@@ -787,7 +782,7 @@ JsonbIteratorInit(char *buffer)
 }
 
 /*
- * qsort comparator to compare JsonbPair values.
+ * qsort_arg() comparator to compare JsonbPair values.
  *
  * Function implemented in terms of compareJsonbStringValue(), and thus the
  * same "arg setting" hack will be applied here in respect of the pair's key
@@ -818,7 +813,8 @@ compareJsonbPair(const void *a, const void *b, void *arg)
  *                   Walk the tree representation of jsonb                 *
  ****************************************************************************/
 static void
-walkUncompressedJsonbDo(JsonbValue * v, walk_jsonb_cb cb, void *cb_arg, uint32 level)
+walkUncompressedJsonbDo(JsonbValue * v, walk_jsonb_cb cb, void *cb_arg,
+                       uint32 level)
 {
    int         i;
 
index 1a7c76976d95eec0e82310f7318ce173a0ee489a..70ce22afdc0cb6d8a847118e43192bc009a99379 100644 (file)
  * the string to indicate key, value, or null values.  (As of 9.1 it might be
  * better to store null values as nulls, but we'll keep it this way for on-disk
  * compatibility.)
+ *
+ * jsonb Keys and elements are treated equivalently when serialized to text
+ * index storage.
  */
-#define ELEMFLAG    'E'
-#define KEYFLAG     'K'
+#define KEYELEMFLAG 'K'
 #define VALFLAG     'V'
 #define NULLFLAG    'N'
 
@@ -88,8 +90,8 @@ typedef struct
 typedef struct
 {
    int32       vl_len_;        /* varlena header (do not touch directly!) */
-   /* header of hash or array jsonb type
-    * array of JEntry follows */
+   /* header of jsonb object or array */
+   /* array of JEntry follows */
 } Jsonb;
 
 struct JsonbValue
@@ -145,7 +147,7 @@ struct JsonbPair
 {
    JsonbValue  key;
    JsonbValue  value;
-   uint32      order;          /* To preserve order of pairs with equal keys */
+   uint32      order;          /* preserves order of pairs with equal keys */
 };
 
 typedef struct ToJsonbState
@@ -163,12 +165,11 @@ typedef struct JsonbIterator
    bool        isScalar;
    char       *data;
    char       *buffer;         /* unparsed buffer */
-
    int         i;
 
    /*
     * Enum members should be freely OR'ed with JB_FLAG_ARRAY/JB_FLAG_JSONB
-    * with possibility of decoding. See optimization in JsonbIteratorGet()
+    * with possibility of decoding.  See optimization in JsonbIteratorGet()
     */
    enum
    {