Fix query-duration memory leak with GIN rescans.
authorHeikki Linnakangas <[email protected]>
Fri, 30 Jan 2015 16:58:23 +0000 (17:58 +0100)
committerHeikki Linnakangas <[email protected]>
Fri, 30 Jan 2015 16:58:23 +0000 (17:58 +0100)
The requiredEntries / additionalEntries arrays were not freed in
freeScanKeys() like other per-key stuff.

It's not obvious, but startScanKey() was only ever called after the keys
have been initialized with ginNewScanKey(). That's why it doesn't need to
worry about freeing existing arrays. The ginIsNewKey() test in gingetbitmap
was never true, because ginrescan free's the existing keys, and it's not OK
to call gingetbitmap twice in a row without calling ginrescan in between.
To make that clear, remove the unnecessary ginIsNewKey(). And just to be
extra sure that nothing funny happens if there is an existing key after all,
call freeScanKeys() to free it if it exists. This makes the code more
straightforward.

(I'm seeing other similar leaks in testing a query that rescans an GIN index
scan, but that's a different issue. This just fixes the obvious leak with
those two arrays.)

Backpatch to 9.4, where GIN fast scan was added.

src/backend/access/gin/ginget.c
src/backend/access/gin/ginscan.c
src/include/access/gin_private.h

index 57d378a42f72b2aa6a99c9c88a49c7d0ed65f737..9d73142ee93897d82f77d21a37e79f5b17604bc2 100644 (file)
@@ -560,6 +560,10 @@ startScan(IndexScanDesc scan)
        }
    }
 
+   /*
+    * Now that we have the estimates for the entry frequencies, finish
+    * initializing the scan keys.
+    */
    for (i = 0; i < so->nkeys; i++)
        startScanKey(ginstate, so, so->keys + i);
 }
@@ -1763,7 +1767,6 @@ scanPendingInsert(IndexScanDesc scan, TIDBitmap *tbm, int64 *ntids)
 }
 
 
-#define GinIsNewKey(s)     ( ((GinScanOpaque) scan->opaque)->keys == NULL )
 #define GinIsVoidRes(s)        ( ((GinScanOpaque) scan->opaque)->isVoidRes )
 
 Datum
@@ -1771,6 +1774,7 @@ gingetbitmap(PG_FUNCTION_ARGS)
 {
    IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
    TIDBitmap  *tbm = (TIDBitmap *) PG_GETARG_POINTER(1);
+   GinScanOpaque so = (GinScanOpaque) scan->opaque;
    int64       ntids;
    ItemPointerData iptr;
    bool        recheck;
@@ -1778,8 +1782,8 @@ gingetbitmap(PG_FUNCTION_ARGS)
    /*
     * Set up the scan keys, and check for unsatisfiable query.
     */
-   if (GinIsNewKey(scan))
-       ginNewScanKey(scan);
+   ginFreeScanKeys(so); /* there should be no keys yet, but just to be sure */
+   ginNewScanKey(scan);
 
    if (GinIsVoidRes(scan))
        PG_RETURN_INT64(0);
index 7ab795ce0d6dfd2c69af1480a597340e9a4de525..b3a2de1edd4d37d72f6b665c8c1d7cd552dbb4f0 100644 (file)
@@ -163,6 +163,10 @@ ginFillScanKey(GinScanOpaque so, OffsetNumber attnum,
    key->curItemMatches = false;
    key->recheckCurItem = false;
    key->isFinished = false;
+   key->nrequired = 0;
+   key->nadditional = 0;
+   key->requiredEntries = NULL;
+   key->additionalEntries = NULL;
 
    ginInitConsistentFunction(ginstate, key);
 
@@ -223,8 +227,8 @@ ginFillScanKey(GinScanOpaque so, OffsetNumber attnum,
    }
 }
 
-static void
-freeScanKeys(GinScanOpaque so)
+void
+ginFreeScanKeys(GinScanOpaque so)
 {
    uint32      i;
 
@@ -237,6 +241,10 @@ freeScanKeys(GinScanOpaque so)
 
        pfree(key->scanEntry);
        pfree(key->entryRes);
+       if (key->requiredEntries)
+           pfree(key->requiredEntries);
+       if (key->additionalEntries)
+           pfree(key->additionalEntries);
    }
 
    pfree(so->keys);
@@ -416,7 +424,7 @@ ginrescan(PG_FUNCTION_ARGS)
    /* remaining arguments are ignored */
    GinScanOpaque so = (GinScanOpaque) scan->opaque;
 
-   freeScanKeys(so);
+   ginFreeScanKeys(so);
 
    if (scankey && scan->numberOfKeys > 0)
    {
@@ -434,7 +442,7 @@ ginendscan(PG_FUNCTION_ARGS)
    IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
    GinScanOpaque so = (GinScanOpaque) scan->opaque;
 
-   freeScanKeys(so);
+   ginFreeScanKeys(so);
 
    MemoryContextDelete(so->tempCtx);
 
index cf2ef805e37b705a9de04db69e4109c4633b95cc..c949c97edec779bfa7220cc81886e5092f110538 100644 (file)
@@ -899,6 +899,7 @@ extern Datum ginrescan(PG_FUNCTION_ARGS);
 extern Datum ginmarkpos(PG_FUNCTION_ARGS);
 extern Datum ginrestrpos(PG_FUNCTION_ARGS);
 extern void ginNewScanKey(IndexScanDesc scan);
+extern void ginFreeScanKeys(GinScanOpaque so);
 
 /* ginget.c */
 extern Datum gingetbitmap(PG_FUNCTION_ARGS);