mirror of
https://github.com/postgres/postgres.git
synced 2025-04-18 13:44:19 +03:00
252 lines
6.7 KiB
C
252 lines
6.7 KiB
C
/*-------------------------------------------------------------------------
|
|
*
|
|
* gistscan.c
|
|
* routines to manage scans on GiST index relations
|
|
*
|
|
*
|
|
* Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
|
*
|
|
* IDENTIFICATION
|
|
* src/backend/access/gist/gistscan.c
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
#include "postgres.h"
|
|
|
|
#include "access/genam.h"
|
|
#include "access/gist_private.h"
|
|
#include "access/gistscan.h"
|
|
#include "access/relscan.h"
|
|
#include "storage/bufmgr.h"
|
|
#include "utils/memutils.h"
|
|
#include "utils/rel.h"
|
|
|
|
|
|
/*
|
|
* RBTree support functions for the GISTSearchTreeItem queue
|
|
*/
|
|
|
|
static int
|
|
GISTSearchTreeItemComparator(const RBNode *a, const RBNode *b, void *arg)
|
|
{
|
|
const GISTSearchTreeItem *sa = (const GISTSearchTreeItem *) a;
|
|
const GISTSearchTreeItem *sb = (const GISTSearchTreeItem *) b;
|
|
IndexScanDesc scan = (IndexScanDesc) arg;
|
|
int i;
|
|
|
|
/* Order according to distance comparison */
|
|
for (i = 0; i < scan->numberOfOrderBys; i++)
|
|
{
|
|
if (sa->distances[i] != sb->distances[i])
|
|
return (sa->distances[i] > sb->distances[i]) ? 1 : -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
GISTSearchTreeItemCombiner(RBNode *existing, const RBNode *newrb, void *arg)
|
|
{
|
|
GISTSearchTreeItem *scurrent = (GISTSearchTreeItem *) existing;
|
|
const GISTSearchTreeItem *snew = (const GISTSearchTreeItem *) newrb;
|
|
GISTSearchItem *newitem = snew->head;
|
|
|
|
/* snew should have just one item in its chain */
|
|
Assert(newitem && newitem->next == NULL);
|
|
|
|
/*
|
|
* If new item is heap tuple, it goes to front of chain; otherwise insert
|
|
* it before the first index-page item, so that index pages are visited
|
|
* in LIFO order, ensuring depth-first search of index pages. See
|
|
* comments in gist_private.h.
|
|
*/
|
|
if (GISTSearchItemIsHeap(*newitem))
|
|
{
|
|
newitem->next = scurrent->head;
|
|
scurrent->head = newitem;
|
|
if (scurrent->lastHeap == NULL)
|
|
scurrent->lastHeap = newitem;
|
|
}
|
|
else if (scurrent->lastHeap == NULL)
|
|
{
|
|
newitem->next = scurrent->head;
|
|
scurrent->head = newitem;
|
|
}
|
|
else
|
|
{
|
|
newitem->next = scurrent->lastHeap->next;
|
|
scurrent->lastHeap->next = newitem;
|
|
}
|
|
}
|
|
|
|
static RBNode *
|
|
GISTSearchTreeItemAllocator(void *arg)
|
|
{
|
|
IndexScanDesc scan = (IndexScanDesc) arg;
|
|
|
|
return palloc(GSTIHDRSZ + sizeof(double) * scan->numberOfOrderBys);
|
|
}
|
|
|
|
static void
|
|
GISTSearchTreeItemDeleter(RBNode *rb, void *arg)
|
|
{
|
|
pfree(rb);
|
|
}
|
|
|
|
|
|
/*
|
|
* Index AM API functions for scanning GiST indexes
|
|
*/
|
|
|
|
Datum
|
|
gistbeginscan(PG_FUNCTION_ARGS)
|
|
{
|
|
Relation r = (Relation) PG_GETARG_POINTER(0);
|
|
int nkeys = PG_GETARG_INT32(1);
|
|
int norderbys = PG_GETARG_INT32(2);
|
|
IndexScanDesc scan;
|
|
GISTScanOpaque so;
|
|
|
|
scan = RelationGetIndexScan(r, nkeys, norderbys);
|
|
|
|
/* 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);
|
|
/* workspaces with size dependent on numberOfOrderBys: */
|
|
so->tmpTreeItem = palloc(GSTIHDRSZ + sizeof(double) * scan->numberOfOrderBys);
|
|
so->distances = palloc(sizeof(double) * scan->numberOfOrderBys);
|
|
so->qual_ok = true; /* in case there are zero keys */
|
|
|
|
scan->opaque = so;
|
|
|
|
PG_RETURN_POINTER(scan);
|
|
}
|
|
|
|
Datum
|
|
gistrescan(PG_FUNCTION_ARGS)
|
|
{
|
|
IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
|
|
ScanKey key = (ScanKey) PG_GETARG_POINTER(1);
|
|
ScanKey orderbys = (ScanKey) PG_GETARG_POINTER(3);
|
|
/* nkeys and norderbys arguments are ignored */
|
|
GISTScanOpaque so = (GISTScanOpaque) scan->opaque;
|
|
int i;
|
|
MemoryContext oldCxt;
|
|
|
|
/* rescan an existing indexscan --- reset state */
|
|
MemoryContextReset(so->queueCxt);
|
|
so->curTreeItem = NULL;
|
|
|
|
/* create new, empty RBTree for search queue */
|
|
oldCxt = MemoryContextSwitchTo(so->queueCxt);
|
|
so->queue = rb_create(GSTIHDRSZ + sizeof(double) * scan->numberOfOrderBys,
|
|
GISTSearchTreeItemComparator,
|
|
GISTSearchTreeItemCombiner,
|
|
GISTSearchTreeItemAllocator,
|
|
GISTSearchTreeItemDeleter,
|
|
scan);
|
|
MemoryContextSwitchTo(oldCxt);
|
|
|
|
so->firstCall = true;
|
|
|
|
/* Update scan key, if a new one is given */
|
|
if (key && scan->numberOfKeys > 0)
|
|
{
|
|
memmove(scan->keyData, key,
|
|
scan->numberOfKeys * sizeof(ScanKeyData));
|
|
|
|
/*
|
|
* Modify the scan key so that the Consistent method is called for
|
|
* all comparisons. The original operator is passed to the Consistent
|
|
* function in the form of its strategy number, which is available
|
|
* from the sk_strategy field, and its subtype from the sk_subtype
|
|
* field.
|
|
*
|
|
* 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).
|
|
*/
|
|
so->qual_ok = true;
|
|
|
|
for (i = 0; i < scan->numberOfKeys; i++)
|
|
{
|
|
ScanKey skey = scan->keyData + i;
|
|
|
|
skey->sk_func = so->giststate->consistentFn[skey->sk_attno - 1];
|
|
|
|
if (skey->sk_flags & SK_ISNULL)
|
|
{
|
|
if (!(skey->sk_flags & (SK_SEARCHNULL | SK_SEARCHNOTNULL)))
|
|
so->qual_ok = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Update order-by key, if a new one is given */
|
|
if (orderbys && scan->numberOfOrderBys > 0)
|
|
{
|
|
memmove(scan->orderByData, orderbys,
|
|
scan->numberOfOrderBys * sizeof(ScanKeyData));
|
|
|
|
/*
|
|
* Modify the order-by key so that the Distance method is called for
|
|
* all comparisons. The original operator is passed to the Distance
|
|
* function in the form of its strategy number, which is available
|
|
* from the sk_strategy field, and its subtype from the sk_subtype
|
|
* field.
|
|
*/
|
|
for (i = 0; i < scan->numberOfOrderBys; i++)
|
|
{
|
|
ScanKey skey = scan->orderByData + i;
|
|
|
|
skey->sk_func = so->giststate->distanceFn[skey->sk_attno - 1];
|
|
|
|
/* Check we actually have a distance function ... */
|
|
if (!OidIsValid(skey->sk_func.fn_oid))
|
|
elog(ERROR, "missing support function %d for attribute %d of index \"%s\"",
|
|
GIST_DISTANCE_PROC, skey->sk_attno,
|
|
RelationGetRelationName(scan->indexRelation));
|
|
}
|
|
}
|
|
|
|
PG_RETURN_VOID();
|
|
}
|
|
|
|
Datum
|
|
gistmarkpos(PG_FUNCTION_ARGS)
|
|
{
|
|
elog(ERROR, "GiST does not support mark/restore");
|
|
PG_RETURN_VOID();
|
|
}
|
|
|
|
Datum
|
|
gistrestrpos(PG_FUNCTION_ARGS)
|
|
{
|
|
elog(ERROR, "GiST does not support mark/restore");
|
|
PG_RETURN_VOID();
|
|
}
|
|
|
|
Datum
|
|
gistendscan(PG_FUNCTION_ARGS)
|
|
{
|
|
IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
|
|
GISTScanOpaque so = (GISTScanOpaque) scan->opaque;
|
|
|
|
freeGISTstate(so->giststate);
|
|
MemoryContextDelete(so->queueCxt);
|
|
MemoryContextDelete(so->tempCxt);
|
|
pfree(so->tmpTreeItem);
|
|
pfree(so->distances);
|
|
pfree(so);
|
|
|
|
PG_RETURN_VOID();
|
|
}
|