aboutsummaryrefslogtreecommitdiff
path: root/src/backend/access/gist/gistscan.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/access/gist/gistscan.c')
-rw-r--r--src/backend/access/gist/gistscan.c107
1 files changed, 91 insertions, 16 deletions
diff --git a/src/backend/access/gist/gistscan.c b/src/backend/access/gist/gistscan.c
index 5071d46150e..e6140a151bb 100644
--- a/src/backend/access/gist/gistscan.c
+++ b/src/backend/access/gist/gistscan.c
@@ -104,20 +104,28 @@ gistbeginscan(PG_FUNCTION_ARGS)
int nkeys = PG_GETARG_INT32(1);
int norderbys = PG_GETARG_INT32(2);
IndexScanDesc scan;
+ GISTSTATE *giststate;
GISTScanOpaque so;
+ MemoryContext oldCxt;
scan = RelationGetIndexScan(r, nkeys, norderbys);
+ /* First, set up a GISTSTATE with a scan-lifespan memory context */
+ giststate = initGISTstate(scan->indexRelation);
+
+ /*
+ * Everything made below is in the scanCxt, or is a child of the scanCxt,
+ * so it'll all go away automatically in gistendscan.
+ */
+ oldCxt = MemoryContextSwitchTo(giststate->scanCxt);
+
/* initialize opaque data */
so = (GISTScanOpaque) palloc0(sizeof(GISTScanOpaqueData));
- so->queueCxt = AllocSetContextCreate(CurrentMemoryContext,
- "GiST queue context",
- ALLOCSET_DEFAULT_MINSIZE,
- ALLOCSET_DEFAULT_INITSIZE,
- ALLOCSET_DEFAULT_MAXSIZE);
- so->tempCxt = createTempGistContext();
- so->giststate = (GISTSTATE *) palloc(sizeof(GISTSTATE));
- initGISTstate(so->giststate, scan->indexRelation);
+ so->giststate = giststate;
+ giststate->tempCxt = createTempGistContext();
+ so->queue = NULL;
+ so->queueCxt = giststate->scanCxt; /* see gistrescan */
+
/* workspaces with size dependent on numberOfOrderBys: */
so->tmpTreeItem = palloc(GSTIHDRSZ + sizeof(double) * scan->numberOfOrderBys);
so->distances = palloc(sizeof(double) * scan->numberOfOrderBys);
@@ -125,6 +133,8 @@ gistbeginscan(PG_FUNCTION_ARGS)
scan->opaque = so;
+ MemoryContextSwitchTo(oldCxt);
+
PG_RETURN_POINTER(scan);
}
@@ -137,12 +147,44 @@ gistrescan(PG_FUNCTION_ARGS)
/* nkeys and norderbys arguments are ignored */
GISTScanOpaque so = (GISTScanOpaque) scan->opaque;
+ bool first_time;
int i;
MemoryContext oldCxt;
/* rescan an existing indexscan --- reset state */
- MemoryContextReset(so->queueCxt);
- so->curTreeItem = NULL;
+
+ /*
+ * The first time through, we create the search queue in the scanCxt.
+ * Subsequent times through, we create the queue in a separate queueCxt,
+ * which is created on the second call and reset on later calls. Thus, in
+ * the common case where a scan is only rescan'd once, we just put the
+ * queue in scanCxt and don't pay the overhead of making a second memory
+ * context. If we do rescan more than once, the first RBTree is just left
+ * for dead until end of scan; this small wastage seems worth the savings
+ * in the common case.
+ */
+ if (so->queue == NULL)
+ {
+ /* first time through */
+ Assert(so->queueCxt == so->giststate->scanCxt);
+ first_time = true;
+ }
+ else if (so->queueCxt == so->giststate->scanCxt)
+ {
+ /* second time through */
+ so->queueCxt = AllocSetContextCreate(so->giststate->scanCxt,
+ "GiST queue context",
+ ALLOCSET_DEFAULT_MINSIZE,
+ ALLOCSET_DEFAULT_INITSIZE,
+ ALLOCSET_DEFAULT_MAXSIZE);
+ first_time = false;
+ }
+ else
+ {
+ /* third or later time through */
+ MemoryContextReset(so->queueCxt);
+ first_time = false;
+ }
/* create new, empty RBTree for search queue */
oldCxt = MemoryContextSwitchTo(so->queueCxt);
@@ -154,11 +196,28 @@ gistrescan(PG_FUNCTION_ARGS)
scan);
MemoryContextSwitchTo(oldCxt);
+ so->curTreeItem = NULL;
so->firstCall = true;
/* Update scan key, if a new one is given */
if (key && scan->numberOfKeys > 0)
{
+ /*
+ * If this isn't the first time through, preserve the fn_extra
+ * pointers, so that if the consistentFns are using them to cache
+ * data, that data is not leaked across a rescan.
+ */
+ if (!first_time)
+ {
+ for (i = 0; i < scan->numberOfKeys; i++)
+ {
+ ScanKey skey = scan->keyData + i;
+
+ so->giststate->consistentFn[skey->sk_attno - 1].fn_extra =
+ skey->sk_func.fn_extra;
+ }
+ }
+
memmove(scan->keyData, key,
scan->numberOfKeys * sizeof(ScanKeyData));
@@ -172,6 +231,10 @@ gistrescan(PG_FUNCTION_ARGS)
* Next, if any of keys is a NULL and that key is not marked with
* SK_SEARCHNULL/SK_SEARCHNOTNULL then nothing can be found (ie, we
* assume all indexable operators are strict).
+ *
+ * Note: we intentionally memcpy the FmgrInfo to sk_func rather than
+ * using fmgr_info_copy. This is so that the fn_extra field gets
+ * preserved across multiple rescans.
*/
so->qual_ok = true;
@@ -192,6 +255,18 @@ gistrescan(PG_FUNCTION_ARGS)
/* Update order-by key, if a new one is given */
if (orderbys && scan->numberOfOrderBys > 0)
{
+ /* As above, preserve fn_extra if not first time through */
+ if (!first_time)
+ {
+ for (i = 0; i < scan->numberOfOrderBys; i++)
+ {
+ ScanKey skey = scan->orderByData + i;
+
+ so->giststate->distanceFn[skey->sk_attno - 1].fn_extra =
+ skey->sk_func.fn_extra;
+ }
+ }
+
memmove(scan->orderByData, orderbys,
scan->numberOfOrderBys * sizeof(ScanKeyData));
@@ -201,6 +276,8 @@ gistrescan(PG_FUNCTION_ARGS)
* function in the form of its strategy number, which is available
* from the sk_strategy field, and its subtype from the sk_subtype
* field.
+ *
+ * See above comment about why we don't use fmgr_info_copy here.
*/
for (i = 0; i < scan->numberOfOrderBys; i++)
{
@@ -239,13 +316,11 @@ gistendscan(PG_FUNCTION_ARGS)
IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
GISTScanOpaque so = (GISTScanOpaque) scan->opaque;
+ /*
+ * freeGISTstate is enough to clean up everything made by gistbeginscan,
+ * as well as the queueCxt if there is a separate context for it.
+ */
freeGISTstate(so->giststate);
- pfree(so->giststate);
- MemoryContextDelete(so->queueCxt);
- MemoryContextDelete(so->tempCxt);
- pfree(so->tmpTreeItem);
- pfree(so->distances);
- pfree(so);
PG_RETURN_VOID();
}