Be careful about freeing memory in comparator
authorPeter Geoghegan <[email protected]>
Fri, 14 Mar 2014 00:43:33 +0000 (17:43 -0700)
committerPeter Geoghegan <[email protected]>
Fri, 14 Mar 2014 00:44:36 +0000 (17:44 -0700)
src/backend/utils/adt/jsonb_op.c
src/backend/utils/adt/jsonb_support.c
src/include/utils/jsonb.h

index 4e97c1cc015ead681d704905ea828327f6a483fe..75e061edfee0aa32425ed6c83be89c6523cd15bc 100644 (file)
@@ -227,9 +227,6 @@ jsonb_cmp(PG_FUNCTION_ARGS)
    }
    else
    {
-       /*
-        * TODO: Think harder about the memory leaked here
-        */
        res = compareJsonbSuperHeaderValue(VARDATA(jb1), VARDATA(jb2));
    }
 
index 3d2ad98d727105e1206b2aa2927b7cf5e8612e78..bee3982c0799f28e3e5fa85cedc9ffb133ab3869 100644 (file)
@@ -177,17 +177,18 @@ compareJsonbScalarValue(JsonbValue * a, JsonbValue * b)
 }
 
 /*
- * Gives consistent ordering of Jsonb values.
+ * BT comparator worker function.  Returns an integer less than, equal to, or
+ * greater than zero, indicating whether a is less than, equal to, or greater
+ * than b.  Consistent with the requirements for a B-Tree operator class
  *
- * Strings are compared lexically, making this suitable as a sort comparator.
- *
- * This is called from B-Tree support function 1.
+ * Strings are compared lexically.  Since this is called from B-Tree support
+ * function 1, we're careful about not leaking memory here.
  */
 int
 compareJsonbSuperHeaderValue(JsonbSuperHeader a, JsonbSuperHeader b)
 {
    JsonbIterator *it1,
-              *it2;
+                 *it2;
    int         res = 0;
 
    it1 = JsonbIteratorInit(a);
@@ -205,7 +206,7 @@ compareJsonbSuperHeaderValue(JsonbSuperHeader a, JsonbSuperHeader b)
 
        if (r1 == r2)
        {
-           if (r1 == 0)
+           if (r1 == WJB_DONE)
                break;          /* equal */
 
            if (v1.type == v2.type)
@@ -250,6 +251,19 @@ compareJsonbSuperHeaderValue(JsonbSuperHeader a, JsonbSuperHeader b)
    }
    while (res == 0);
 
+   while (it1 != NULL)
+   {
+       JsonbIterator *i = it1->parent;
+       pfree(it1);
+       it1 = i;
+   }
+   while (it2 != NULL)
+   {
+       JsonbIterator *i = it2->parent;
+       pfree(it2);
+       it2 = i;
+   }
+
    return res;
 }
 
@@ -544,13 +558,13 @@ getIthJsonbValueFromSuperHeader(JsonbSuperHeader sheader, uint32 flags,
 JsonbValue *
 pushJsonbValue(ToJsonbState ** state, int r, JsonbValue * v)
 {
-   JsonbValue *h = NULL;
+   JsonbValue *result = NULL;
 
    switch (r)
    {
        case WJB_BEGIN_ARRAY:
            *state = pushState(state);
-           h = &(*state)->v;
+           result = &(*state)->v;
            (*state)->v.type = jbvArray;
            (*state)->v.size = 3 * sizeof(JEntry);
            (*state)->v.array.nElems = 0;
@@ -562,7 +576,7 @@ pushJsonbValue(ToJsonbState ** state, int r, JsonbValue * v)
            break;
        case WJB_BEGIN_OBJECT:
            *state = pushState(state);
-           h = &(*state)->v;
+           result = &(*state)->v;
            (*state)->v.type = jbvObject;
            (*state)->v.size = 3 * sizeof(JEntry);
            (*state)->v.object.nPairs = 0;
@@ -584,13 +598,13 @@ pushJsonbValue(ToJsonbState ** state, int r, JsonbValue * v)
            appendElement(*state, v);
            break;
        case WJB_END_OBJECT:
-           h = &(*state)->v;
+           result = &(*state)->v;
            /*
             * When v != NULL and control reaches here, keys should already be
             * sorted
             */
            if (v == NULL)
-               uniqueifyJsonbObject(h);
+               uniqueifyJsonbObject(result);
 
            /*
             * No break statement here - fall through and perform those steps
@@ -600,7 +614,7 @@ pushJsonbValue(ToJsonbState ** state, int r, JsonbValue * v)
             * in the same fashion as arrays.
             */
        case WJB_END_ARRAY:
-           h = &(*state)->v;
+           result = &(*state)->v;
 
            /*
             * Pop stack and push current array/"object" as value in parent
@@ -612,10 +626,10 @@ pushJsonbValue(ToJsonbState ** state, int r, JsonbValue * v)
                switch ((*state)->v.type)
                {
                    case jbvArray:
-                       appendElement(*state, h);
+                       appendElement(*state, result);
                        break;
                    case jbvObject:
-                       appendValue(*state, h);
+                       appendValue(*state, result);
                        break;
                    default:
                        elog(ERROR, "invalid jsonb container type");
@@ -626,12 +640,14 @@ pushJsonbValue(ToJsonbState ** state, int r, JsonbValue * v)
            elog(ERROR, "invalid jsonb container type");
    }
 
-   return h;
+   return result;
 }
 
 /*
  * Given a Jsonb superheader, expand to JsonbIterator to iterate over items
  * fully expanded to in-memory representation for manipulation.
+ *
+ * See JsonbIteratorNext() for notes on memory management.
  */
 JsonbIterator *
 JsonbIteratorInit(JsonbSuperHeader sheader)
@@ -651,7 +667,13 @@ JsonbIteratorInit(JsonbSuperHeader sheader)
  * back a child iterator palloc()'d here instead.  The function can be relied
  * on to free those child iterators, lest the memory allocated for highly
  * nested objects become unreasonable, but only if callers don't end iteration
- * early.
+ * early (by breaking upon having found something in a search, for example).
+ *
+ * Callers in such a scenario, that are particularly sensitive to leaking
+ * memory in a long-lived context may walk the ancestral tree from the final
+ * iterator we left them with to its oldest ancestor, pfree()ing as they go.
+ * They can depended on having any other memory previously allocated for
+ * iterators but not in that line having already been freed here.
  */
 int
 JsonbIteratorNext(JsonbIterator ** it, JsonbValue * v, bool skipNested)
@@ -1242,16 +1264,17 @@ formIterIsContainer(JsonbIterator ** it, JsonbValue * v, JEntry * e,
    {
        /*
         * Must be container type, so setup caller's iterator to point to that,
-        * and return indication of that
+        * and return indication of that.
+        *
+        * Get child iterator.
         */
-       JsonbIterator *nit = palloc(sizeof(JsonbIterator));
+       JsonbIterator *child = palloc(sizeof(JsonbIterator));
 
-       /* Get child iterator */
-       iteratorFromContainerBuf(nit,
+       iteratorFromContainerBuf(child,
                                 (*it)->dataProper + INTALIGN(JBE_OFF(*e)));
 
-       nit->parent = *it;
-       *it = nit;
+       child->parent = *it;
+       *it = child;
 
        return true;
    }
index 49071b756e9299986b922a277ad060a1f7bb859d..5ca03eedf4d3b5b6126f975161241490cb2f0ae0 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, 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.)
+ * to indicate key/element, value, or SQL NULL value.
  *
- * jsonb Keys and elements are treated equivalently when serialized to text
- * index storage.
+ * 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.
  */
 #define KEYELEMFLAG 'K'
 #define VALFLAG     'V'
@@ -101,20 +101,18 @@ typedef   char*  JsonbSuperHeader;
  *
  * We have an abstraction called a "superheader".  This is a pointer that
  * conventionally points to the first item after our 4-byte uncompressed
- * varlena header, from which we can read uint32 values through bitwise
- * operations.
+ * varlena header, from which we can read flags using bitwise operations.
  *
- * Sometimes we pass a superheader reference to a function, and it doesn't
+ * Frequently, we pass a superheader reference to a function, and it doesn't
  * matter if it points to just after the start of a Jsonb, or to a Jentry.
  * Either way, the type punning works and the superheader/header metadata is
  * used to operate on an underlying JsonbValue.
  *
- * In a few contexts, when passing a superheader, there actually is an
- * assumption that it really does point to just past vl_len_ in a Jsonb.  We
- * assert that it's "superheader sane" in those contexts.  In general, this is
- * expected to work just fine, as care has been taken to make the nested layout
- * consistent to the extent that it matters between the least nested level
- * (Jsonb), and deeper nested levels (Jentry).
+ * In a few contexts, when passing a superheader, there may be an assumption
+ * that it really does point to just past vl_len_ in a Jsonb, but that has
+ * nothing to do with layout.  Care has been taken to make the nested layout
+ * consistent (to the extent that it matters) between the least nested level
+ * (Jsonb superheaders), and deeper nesting levels (Jentry headers).
  */
 
 typedef struct
@@ -125,7 +123,7 @@ typedef struct
 } Jsonb;
 
 /*
- * JEntry: there is one of these for each key _and_ value in a jsonb object
+ * JEntry: there is one of these for each key _and_ value in a jsonb object.
  *
  * The position offset points to the _end_ so that we can get the length by
  * subtraction from the previous entry.     The JENTRY_ISFIRST flag indicates if
@@ -190,8 +188,8 @@ struct JsonbValue
 /* Pair within an Object */
 struct JsonbPair
 {
-   JsonbValue  key;
-   JsonbValue  value;
+   JsonbValue  key;            /* Must be a jbvString */
+   JsonbValue  value;          /* May be of any type */
    uint32      order;          /* preserves order of pairs with equal keys */
 };
 
@@ -225,7 +223,7 @@ typedef struct JsonbIterator
    int         i;
 
    /* Current value */
-   uint32      containerType; /* Never of value JB_FLAG_SCALAR, since
+   uint32      containerType; /* Never of value JB_FSCALAR, since
                                * scalars will appear in pseudo-arrays */
    uint32      nElems;        /* Number of elements in metaArray
                                * (we * 2 for pairs within objects) */
@@ -233,7 +231,7 @@ typedef struct JsonbIterator
    JEntry     *meta;
 
    /*
-    * Data proper.  Note that this points just past end of metaArray.  We use
+    * Data proper.  Note that this points just past end of meta array.  We use
     * "meta" metadata (Jentrys) with JBE_OFF() macro to find appropriate
     * offsets into this array.
     */