Unpack jbvBinary objects passed to pushJsonbValue
authorAndrew Dunstan
Fri, 22 May 2015 14:21:41 +0000 (10:21 -0400)
committerAndrew Dunstan
Fri, 22 May 2015 14:21:41 +0000 (10:21 -0400)
pushJsonbValue was accepting jbvBinary objects passed as WJB_ELEM or
WJB_VALUE data. While this succeeded, when those objects were later
encountered in attempting to convert the result to Jsonb, errors
occurred. With this change we ghuarantee that a JSonbValue constructed
from calls to pushJsonbValue does not contain any jbvBinary objects.
This cures a problem observed with jsonb_delete.

This means callers of pushJsonbValue no longer need to perform this
unpacking themselves. A subsequent patch will perform some cleanup in
that area.

The error was not triggered by any 9.4 code, but this is a publicly
visible routine, and so the error could be exercised by third party
code, therefore backpatch to 9.4.

Bug report from Peter Geoghegan, fix by me.

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

index 5cd9140896b1a8c79e27504cf0df993365199615..974e38652496001132df550a7edfa682e2710ab3 100644 (file)
@@ -57,6 +57,9 @@ static void appendElement(JsonbParseState *pstate, JsonbValue *scalarVal);
 static int lengthCompareJsonbStringValue(const void *a, const void *b);
 static int lengthCompareJsonbPair(const void *a, const void *b, void *arg);
 static void uniqueifyJsonbObject(JsonbValue *object);
+static JsonbValue *pushJsonbValueScalar(JsonbParseState **pstate,
+                                       JsonbIteratorToken seq,
+                                       JsonbValue *scalarVal);
 
 /*
  * Turn an in-memory JsonbValue into a Jsonb for on-disk storage.
@@ -503,10 +506,43 @@ fillJsonbValue(JsonbContainer *container, int index,
  *
  * Only sequential tokens pertaining to non-container types should pass a
  * JsonbValue.  There is one exception -- WJB_BEGIN_ARRAY callers may pass a
- * "raw scalar" pseudo array to append that.
+ * "raw scalar" pseudo array to append it - the actual scalar should be passed
+ * next and it will be added as the only member of the array.
+ *
+ * Values of type jvbBinary, which are rolled up arrays and objects,
+ * are unpacked before being added to the result.
  */
 JsonbValue *
 pushJsonbValue(JsonbParseState **pstate, JsonbIteratorToken seq,
+              JsonbValue *jbval)
+{
+   JsonbIterator *it;
+   JsonbValue *res = NULL;
+   JsonbValue v;
+   JsonbIteratorToken tok;
+
+   if (!jbval || (seq != WJB_ELEM && seq != WJB_VALUE) ||
+       jbval->type != jbvBinary)
+   {
+       /* drop through */
+       return pushJsonbValueScalar(pstate, seq, jbval);
+   }
+
+   /* unpack the binary and add each piece to the pstate */
+   it = JsonbIteratorInit(jbval->val.binary.data);
+   while ((tok = JsonbIteratorNext(&it, &v, false)) != WJB_DONE)
+       res = pushJsonbValueScalar(pstate, tok,
+                                  tok < WJB_BEGIN_ARRAY ? &v : NULL);
+
+   return res;
+}
+
+/*
+ * Do the actual pushing, with only scalar or pseudo-scalar-array values
+ * accepted.
+ */
+static JsonbValue *
+pushJsonbValueScalar(JsonbParseState **pstate, JsonbIteratorToken seq,
               JsonbValue *scalarVal)
 {
    JsonbValue *result = NULL;
@@ -549,13 +585,11 @@ pushJsonbValue(JsonbParseState **pstate, JsonbIteratorToken seq,
            appendKey(*pstate, scalarVal);
            break;
        case WJB_VALUE:
-           Assert(IsAJsonbScalar(scalarVal) ||
-                  scalarVal->type == jbvBinary);
+           Assert(IsAJsonbScalar(scalarVal));
            appendValue(*pstate, scalarVal);
            break;
        case WJB_ELEM:
-           Assert(IsAJsonbScalar(scalarVal) ||
-                  scalarVal->type == jbvBinary);
+           Assert(IsAJsonbScalar(scalarVal));
            appendElement(*pstate, scalarVal);
            break;
        case WJB_END_OBJECT:
index 7b561759f7f22d01f80de12369250f1fa972fbcf..b02934a1aef55f9f7f862c7e8703da465d91bb0b 100644 (file)
@@ -418,7 +418,7 @@ extern JsonbValue *findJsonbValueFromContainer(JsonbContainer *sheader,
 extern JsonbValue *getIthJsonbValueFromContainer(JsonbContainer *sheader,
                              uint32 i);
 extern JsonbValue *pushJsonbValue(JsonbParseState **pstate,
-              JsonbIteratorToken seq, JsonbValue *scalarVal);
+              JsonbIteratorToken seq, JsonbValue *jbVal);
 extern JsonbIterator *JsonbIteratorInit(JsonbContainer *container);
 extern JsonbIteratorToken JsonbIteratorNext(JsonbIterator **it, JsonbValue *val,
                  bool skipNested);