Header And Logo

PostgreSQL
| The world's most advanced open source database.

Functions

gistscan.c File Reference

#include "postgres.h"
#include "access/gist_private.h"
#include "access/gistscan.h"
#include "access/relscan.h"
#include "utils/memutils.h"
#include "utils/rel.h"
Include dependency graph for gistscan.c:

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 RBNodeGISTSearchTreeItemAllocator (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)

Function Documentation

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;
    }
}

static int GISTSearchTreeItemComparator ( const RBNode a,
const RBNode b,
void *  arg 
) [static]

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);
}