Fix existence-element-type confusion
authorPeter Geoghegan <[email protected]>
Wed, 19 Mar 2014 04:33:13 +0000 (21:33 -0700)
committerPeter Geoghegan <[email protected]>
Wed, 19 Mar 2014 04:33:13 +0000 (21:33 -0700)
The fix proposed here simply has the GIN code treat both Jsonb keys
(which are always strings), and Jsonb elements that happen to be strings
as keys, while non-string elements are treated as values.  We continue
to treat object values as values within the GIN Jsonb code in all
circumstances.

The definition of key used here is the Jsonb GIN default opclass
definition.  This is a definition distinct from both the generic GIN
definition of a key, and distinct from the generic Jsonb definition of a
key.  Therefore, nothing in the default GIN operator class precludes a
more general notion of containment that allows for consider non-string
JsonbValues (even if that's not terribly compelling).

src/backend/utils/adt/jsonb_gin.c
src/include/utils/jsonb.h

index 57b4cd0b57749881bb4048f993d6b1a4bfc7be50..034f3935e6bebad581c56685ae55f283ee06af17 100644 (file)
@@ -91,30 +91,55 @@ gin_extract_jsonb(PG_FUNCTION_ARGS)
        }
 
        /*
-        * 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
-        * definition of containment).
+        * Serialize keys and elements equivalently,  but only when elements
+        * are Jsonb strings.  Otherwise, serialize elements as values.  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 definition of containment).
+        * However, our definition of existence does not allow for checking the
+        * existence of a non-jbvString element (just like the definition of
+        * the underlying operator), because the operator takes a text rhs
+        * argument (which we take as a proxy for an equivalent Jsonb string).
         *
-        * See remarks above findJsonbValueFromSuperHeader() for information on
-        * our definition of containment as it relates to elements and
-        * 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.
+        * The way existence is represented does not preclude an alternative
+        * existence operator, that takes as its rhs value an arbitrarily
+        * internally-typed Jsonb value.  The only reason that isn't the case
+        * here is that the existence operator is only really intended to
+        * determine if an object has a certain key (pair keys are of course
+        * invariably strings), which is extended to arrays.  You could think
+        * of the default Jsonb definition of existence as being equivalent to
+        * a definition where all array elements are keys that we can check the
+        * existence of, while just forbidding non-string notation.  This
+        * inflexibility prevents the user from having to qualify that the rhs
+        * string is a raw scalar string (that is, naturally no internal string
+        * quoting in required for the text argument), and allows us to not set
+        * the reset flag for JsonbExistsStrategyNumber, since we know that
+        * keys are strings for both objects and arrays, and don't have to
+        * further account for type mismatch.  This latter reason makes it less
+        * than tempting to tighten up the definition of existence to preclude
+        * array elements entirely.  The recheck flag is set for
+        * JsonbContainsStrategyNumber, which can be used to search for
+        * individual non-string array elements, or even a heterogeneous subset
+        * of the array composed of a random selection of element keys
+        * (strings) and element values (every other type).
+        *
+        * See remarks above findJsonbValueFromSuperHeader() for more
+        * information on our definition of containment as it relates to
+        * elements and key/value pairs (this is orthogonal to the key typing
+        * issue;  in principle the internal containment infrastructure could
+        * accommodate containment of non-string elements).
         */
        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_key(&v, JKEYELEM));
                break;
+           case WJB_ELEM:
+               if (v.type == jbvString)
+                   entries[i++] = PointerGetDatum(make_scalar_key(&v, JKEYELEM));
+               else
+                   entries[i++] = PointerGetDatum(make_scalar_key(&v, JVAL));
+               break;
            case WJB_VALUE:
                entries[i++] = PointerGetDatum(make_scalar_key(&v, JVAL));
                break;
index bc99b985d316dc4056e5b99b69e1b3edb88ceaf2..48efba85088c969ee173ece20370d61cecc04283 100644 (file)
 
 /*
  * When using a GIN index for jsonb, we choose to index both keys and values.
- * The storage format is "text" values, with K, V, or N prepended to the string
- * to indicate key/element, value, or SQL NULL value.
+ * The storage format is text, with K, V, or N prepended to the string to
+ * indicate key/element, value/element, or SQL NULL value.
  *
- * Jsonb Keys and elements are treated equivalently when serialized to text
- * index storage.  One day we may wish to create an opclass that only indexes
- * keys (or only values), but for now it's always keys and values together, or
- * just array elements.
+ * Jsonb Keys and string array elements are treated equivalently when
+ * serialized to text index storage.  One day we may wish to create an opclass
+ * that only indexes keys (or only values), but for now it's always keys and
+ * values together, or just array elements.
  */
 #define JKEYELEM   'K'
 #define JVAL       'V'