Commit d88976cf authored by Heikki Linnakangas's avatar Heikki Linnakangas

Use a separate memory context for GIN scan keys.

It was getting tedious to track and release all the different things that
form a scan key. We were leaking at least the queryCategories array, and
possibly more, on a rescan. That was visible if a GIN index was used in a
nested loop join. This also protects from leaks in extractQuery method.

No backpatching, given the lack of complaints from the field. Maybe later,
after this has received more field testing.
parent 57fe2468
...@@ -497,7 +497,7 @@ startScanKey(GinState *ginstate, GinScanOpaque so, GinScanKey key) ...@@ -497,7 +497,7 @@ startScanKey(GinState *ginstate, GinScanOpaque so, GinScanKey key)
} }
/* i is now the last required entry. */ /* i is now the last required entry. */
MemoryContextSwitchTo(oldCtx); MemoryContextSwitchTo(so->keyCtx);
key->nrequired = i + 1; key->nrequired = i + 1;
key->nadditional = key->nentries - key->nrequired; key->nadditional = key->nentries - key->nrequired;
...@@ -515,11 +515,14 @@ startScanKey(GinState *ginstate, GinScanOpaque so, GinScanKey key) ...@@ -515,11 +515,14 @@ startScanKey(GinState *ginstate, GinScanOpaque so, GinScanKey key)
} }
else else
{ {
MemoryContextSwitchTo(so->keyCtx);
key->nrequired = 1; key->nrequired = 1;
key->nadditional = 0; key->nadditional = 0;
key->requiredEntries = palloc(1 * sizeof(GinScanEntry)); key->requiredEntries = palloc(1 * sizeof(GinScanEntry));
key->requiredEntries[0] = key->scanEntry[0]; key->requiredEntries[0] = key->scanEntry[0];
} }
MemoryContextSwitchTo(oldCtx);
} }
static void static void
......
...@@ -44,6 +44,11 @@ ginbeginscan(PG_FUNCTION_ARGS) ...@@ -44,6 +44,11 @@ ginbeginscan(PG_FUNCTION_ARGS)
ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_MINSIZE,
ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_INITSIZE,
ALLOCSET_DEFAULT_MAXSIZE); ALLOCSET_DEFAULT_MAXSIZE);
so->keyCtx = AllocSetContextCreate(CurrentMemoryContext,
"Gin scan key context",
ALLOCSET_DEFAULT_MINSIZE,
ALLOCSET_DEFAULT_INITSIZE,
ALLOCSET_DEFAULT_MAXSIZE);
initGinState(&so->ginstate, scan->indexRelation); initGinState(&so->ginstate, scan->indexRelation);
scan->opaque = so; scan->opaque = so;
...@@ -227,6 +232,9 @@ ginFillScanKey(GinScanOpaque so, OffsetNumber attnum, ...@@ -227,6 +232,9 @@ ginFillScanKey(GinScanOpaque so, OffsetNumber attnum,
} }
} }
/*
* Release current scan keys, if any.
*/
void void
ginFreeScanKeys(GinScanOpaque so) ginFreeScanKeys(GinScanOpaque so)
{ {
...@@ -235,38 +243,22 @@ ginFreeScanKeys(GinScanOpaque so) ...@@ -235,38 +243,22 @@ ginFreeScanKeys(GinScanOpaque so)
if (so->keys == NULL) if (so->keys == NULL)
return; return;
for (i = 0; i < so->nkeys; i++)
{
GinScanKey key = so->keys + i;
pfree(key->scanEntry);
pfree(key->entryRes);
if (key->requiredEntries)
pfree(key->requiredEntries);
if (key->additionalEntries)
pfree(key->additionalEntries);
}
pfree(so->keys);
so->keys = NULL;
so->nkeys = 0;
for (i = 0; i < so->totalentries; i++) for (i = 0; i < so->totalentries; i++)
{ {
GinScanEntry entry = so->entries[i]; GinScanEntry entry = so->entries[i];
if (entry->buffer != InvalidBuffer) if (entry->buffer != InvalidBuffer)
ReleaseBuffer(entry->buffer); ReleaseBuffer(entry->buffer);
if (entry->list)
pfree(entry->list);
if (entry->matchIterator) if (entry->matchIterator)
tbm_end_iterate(entry->matchIterator); tbm_end_iterate(entry->matchIterator);
if (entry->matchBitmap) if (entry->matchBitmap)
tbm_free(entry->matchBitmap); tbm_free(entry->matchBitmap);
pfree(entry);
} }
pfree(so->entries); MemoryContextResetAndDeleteChildren(so->keyCtx);
so->keys = NULL;
so->nkeys = 0;
so->entries = NULL; so->entries = NULL;
so->totalentries = 0; so->totalentries = 0;
} }
...@@ -278,6 +270,14 @@ ginNewScanKey(IndexScanDesc scan) ...@@ -278,6 +270,14 @@ ginNewScanKey(IndexScanDesc scan)
GinScanOpaque so = (GinScanOpaque) scan->opaque; GinScanOpaque so = (GinScanOpaque) scan->opaque;
int i; int i;
bool hasNullQuery = false; bool hasNullQuery = false;
MemoryContext oldCtx;
/*
* Allocate all the scan key information in the key context. (If
* extractQuery leaks anything there, it won't be reset until the end of
* scan or rescan, but that's OK.)
*/
oldCtx = MemoryContextSwitchTo(so->keyCtx);
/* if no scan keys provided, allocate extra EVERYTHING GinScanKey */ /* if no scan keys provided, allocate extra EVERYTHING GinScanKey */
so->keys = (GinScanKey) so->keys = (GinScanKey)
...@@ -412,6 +412,8 @@ ginNewScanKey(IndexScanDesc scan) ...@@ -412,6 +412,8 @@ ginNewScanKey(IndexScanDesc scan)
RelationGetRelationName(scan->indexRelation)))); RelationGetRelationName(scan->indexRelation))));
} }
MemoryContextSwitchTo(oldCtx);
pgstat_count_index_scan(scan->indexRelation); pgstat_count_index_scan(scan->indexRelation);
} }
...@@ -445,6 +447,7 @@ ginendscan(PG_FUNCTION_ARGS) ...@@ -445,6 +447,7 @@ ginendscan(PG_FUNCTION_ARGS)
ginFreeScanKeys(so); ginFreeScanKeys(so);
MemoryContextDelete(so->tempCtx); MemoryContextDelete(so->tempCtx);
MemoryContextDelete(so->keyCtx);
pfree(so); pfree(so);
......
...@@ -888,6 +888,8 @@ typedef struct GinScanOpaqueData ...@@ -888,6 +888,8 @@ typedef struct GinScanOpaqueData
uint32 totalentries; uint32 totalentries;
uint32 allocentries; /* allocated length of entries[] */ uint32 allocentries; /* allocated length of entries[] */
MemoryContext keyCtx; /* used to hold key and entry data */
bool isVoidRes; /* true if query is unsatisfiable */ bool isVoidRes; /* true if query is unsatisfiable */
} GinScanOpaqueData; } GinScanOpaqueData;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment