#include "postgres.h"
#include "access/gist_private.h"
#include "access/gistscan.h"
#include "access/relscan.h"
#include "utils/memutils.h"
#include "utils/rel.h"
Go to the source code of this file.
Functions | |
static int | GISTSearchTreeItemComparator (const RBNode *a, const RBNode *b, void *arg) |
static void | GISTSearchTreeItemCombiner (RBNode *existing, const RBNode *newrb, void *arg) |
static RBNode * | GISTSearchTreeItemAllocator (void *arg) |
static void | GISTSearchTreeItemDeleter (RBNode *rb, void *arg) |
Datum | gistbeginscan (PG_FUNCTION_ARGS) |
Datum | gistrescan (PG_FUNCTION_ARGS) |
Datum | gistmarkpos (PG_FUNCTION_ARGS) |
Datum | gistrestrpos (PG_FUNCTION_ARGS) |
Datum | gistendscan (PG_FUNCTION_ARGS) |
Datum gistbeginscan | ( | PG_FUNCTION_ARGS | ) |
Definition at line 101 of file gistscan.c.
References createTempGistContext(), GISTScanOpaqueData::distances, GISTScanOpaqueData::giststate, GSTIHDRSZ, IndexScanDescData::indexRelation, initGISTstate(), MemoryContextSwitchTo(), IndexScanDescData::numberOfOrderBys, IndexScanDescData::opaque, palloc(), palloc0(), PG_GETARG_INT32, PG_GETARG_POINTER, PG_RETURN_POINTER, GISTScanOpaqueData::qual_ok, GISTScanOpaqueData::queue, GISTScanOpaqueData::queueCxt, RelationGetIndexScan(), GISTSTATE::scanCxt, GISTSTATE::tempCxt, and GISTScanOpaqueData::tmpTreeItem.
{ Relation r = (Relation) PG_GETARG_POINTER(0); 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->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); so->qual_ok = true; /* in case there are zero keys */ scan->opaque = so; MemoryContextSwitchTo(oldCxt); PG_RETURN_POINTER(scan); }
Datum gistendscan | ( | PG_FUNCTION_ARGS | ) |
Definition at line 314 of file gistscan.c.
References freeGISTstate(), GISTScanOpaqueData::giststate, IndexScanDescData::opaque, PG_GETARG_POINTER, and PG_RETURN_VOID.
{ 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); PG_RETURN_VOID(); }
Datum gistmarkpos | ( | PG_FUNCTION_ARGS | ) |
Definition at line 300 of file gistscan.c.
References elog, ERROR, and PG_RETURN_VOID.
{ elog(ERROR, "GiST does not support mark/restore"); PG_RETURN_VOID(); }
Datum gistrescan | ( | PG_FUNCTION_ARGS | ) |
Definition at line 142 of file gistscan.c.
References ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE, ALLOCSET_DEFAULT_MINSIZE, AllocSetContextCreate(), Assert, GISTSTATE::consistentFn, GISTScanOpaqueData::curTreeItem, GISTSTATE::distanceFn, elog, ERROR, GISTScanOpaqueData::firstCall, FmgrInfo::fn_extra, FmgrInfo::fn_oid, GIST_DISTANCE_PROC, GISTSearchTreeItemAllocator(), GISTSearchTreeItemCombiner(), GISTSearchTreeItemComparator(), GISTSearchTreeItemDeleter(), GISTScanOpaqueData::giststate, GSTIHDRSZ, i, IndexScanDescData::indexRelation, IndexScanDescData::keyData, memmove, MemoryContextReset(), MemoryContextSwitchTo(), NULL, IndexScanDescData::numberOfKeys, IndexScanDescData::numberOfOrderBys, OidIsValid, IndexScanDescData::opaque, IndexScanDescData::orderByData, PG_GETARG_POINTER, PG_RETURN_VOID, GISTScanOpaqueData::qual_ok, GISTScanOpaqueData::queue, GISTScanOpaqueData::queueCxt, rb_create(), RelationGetRelationName, GISTSTATE::scanCxt, ScanKeyData::sk_attno, ScanKeyData::sk_flags, ScanKeyData::sk_func, SK_ISNULL, SK_SEARCHNOTNULL, and SK_SEARCHNULL.
{ 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; bool first_time; int i; MemoryContext oldCxt; /* rescan an existing indexscan --- reset state */ /* * 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); so->queue = rb_create(GSTIHDRSZ + sizeof(double) * scan->numberOfOrderBys, GISTSearchTreeItemComparator, GISTSearchTreeItemCombiner, GISTSearchTreeItemAllocator, GISTSearchTreeItemDeleter, 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)); /* * 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). * * 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; 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) { /* 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)); /* * 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. * * See above comment about why we don't use fmgr_info_copy here. */ 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 gistrestrpos | ( | PG_FUNCTION_ARGS | ) |
Definition at line 307 of file gistscan.c.
References elog, ERROR, and PG_RETURN_VOID.
{ elog(ERROR, "GiST does not support mark/restore"); PG_RETURN_VOID(); }
static RBNode* GISTSearchTreeItemAllocator | ( | void * | arg | ) | [static] |
Definition at line 82 of file gistscan.c.
References GSTIHDRSZ, IndexScanDescData::numberOfOrderBys, and palloc().
Referenced by gistrescan().
{ IndexScanDesc scan = (IndexScanDesc) arg; return palloc(GSTIHDRSZ + sizeof(double) * scan->numberOfOrderBys); }
static void GISTSearchTreeItemCombiner | ( | RBNode * | existing, | |
const RBNode * | newrb, | |||
void * | arg | |||
) | [static] |
Definition at line 47 of file gistscan.c.
References Assert, GISTSearchItemIsHeap, GISTSearchTreeItem::head, GISTSearchTreeItem::lastHeap, GISTSearchItem::next, and NULL.
Referenced by gistrescan().
{ 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; } }
Definition at line 29 of file gistscan.c.
References GISTSearchTreeItem::distances, i, and IndexScanDescData::numberOfOrderBys.
Referenced by gistrescan().
{ 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 GISTSearchTreeItemDeleter | ( | RBNode * | rb, | |
void * | arg | |||
) | [static] |
Definition at line 90 of file gistscan.c.
References pfree().
Referenced by gistrescan().
{ pfree(rb); }