From: Peter Geoghegan Date: Fri, 14 Mar 2014 00:43:33 +0000 (-0700) Subject: Be careful about freeing memory in comparator X-Git-Url: http://git.postgresql.org/gitweb/static/gitweb.js?a=commitdiff_plain;h=16923dd7caf91b130eff7bcc6b5b36e6a9f7bd10;p=users%2Fandresfreund%2Fpostgres.git Be careful about freeing memory in comparator --- diff --git a/src/backend/utils/adt/jsonb_op.c b/src/backend/utils/adt/jsonb_op.c index 4e97c1cc01..75e061edfe 100644 --- a/src/backend/utils/adt/jsonb_op.c +++ b/src/backend/utils/adt/jsonb_op.c @@ -227,9 +227,6 @@ jsonb_cmp(PG_FUNCTION_ARGS) } else { - /* - * TODO: Think harder about the memory leaked here - */ res = compareJsonbSuperHeaderValue(VARDATA(jb1), VARDATA(jb2)); } diff --git a/src/backend/utils/adt/jsonb_support.c b/src/backend/utils/adt/jsonb_support.c index 3d2ad98d72..bee3982c07 100644 --- a/src/backend/utils/adt/jsonb_support.c +++ b/src/backend/utils/adt/jsonb_support.c @@ -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; } diff --git a/src/include/utils/jsonb.h b/src/include/utils/jsonb.h index 49071b756e..5ca03eedf4 100644 --- a/src/include/utils/jsonb.h +++ b/src/include/utils/jsonb.h @@ -67,12 +67,12 @@ /* * 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. */