Header And Logo

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

Functions

ginscan.c File Reference

#include "postgres.h"
#include "access/gin_private.h"
#include "access/relscan.h"
#include "pgstat.h"
#include "utils/memutils.h"
#include "utils/rel.h"
Include dependency graph for ginscan.c:

Go to the source code of this file.

Functions

Datum ginbeginscan (PG_FUNCTION_ARGS)
static GinScanEntry ginFillScanEntry (GinScanOpaque so, OffsetNumber attnum, StrategyNumber strategy, int32 searchMode, Datum queryKey, GinNullCategory queryCategory, bool isPartialMatch, Pointer extra_data)
static void ginFillScanKey (GinScanOpaque so, OffsetNumber attnum, StrategyNumber strategy, int32 searchMode, Datum query, uint32 nQueryValues, Datum *queryValues, GinNullCategory *queryCategories, bool *partial_matches, Pointer *extra_data)
static void freeScanKeys (GinScanOpaque so)
void ginNewScanKey (IndexScanDesc scan)
Datum ginrescan (PG_FUNCTION_ARGS)
Datum ginendscan (PG_FUNCTION_ARGS)
Datum ginmarkpos (PG_FUNCTION_ARGS)
Datum ginrestrpos (PG_FUNCTION_ARGS)

Function Documentation

static void freeScanKeys ( GinScanOpaque  so  )  [static]
Datum ginbeginscan ( PG_FUNCTION_ARGS   ) 
Datum ginendscan ( PG_FUNCTION_ARGS   ) 
static GinScanEntry ginFillScanEntry ( GinScanOpaque  so,
OffsetNumber  attnum,
StrategyNumber  strategy,
int32  searchMode,
Datum  queryKey,
GinNullCategory  queryCategory,
bool  isPartialMatch,
Pointer  extra_data 
) [static]

Definition at line 59 of file ginscan.c.

References GinScanOpaqueData::allocentries, GinScanEntryData::attnum, GinScanEntryData::buffer, GinScanEntryData::curItem, GinScanOpaqueData::entries, GinScanEntryData::extra_data, ginCompareEntries(), GinScanOpaqueData::ginstate, i, GinScanEntryData::isFinished, GinScanEntryData::isPartialMatch, ItemPointerSetMin, GinScanEntryData::list, GinScanEntryData::matchBitmap, GinScanEntryData::matchIterator, GinScanEntryData::matchResult, GinScanEntryData::nlist, NULL, GinScanEntryData::offset, palloc(), GinScanEntryData::queryCategory, GinScanEntryData::queryKey, GinScanEntryData::reduceResult, repalloc(), GinScanEntryData::searchMode, GinScanEntryData::strategy, and GinScanOpaqueData::totalentries.

Referenced by ginFillScanKey().

{
    GinState   *ginstate = &so->ginstate;
    GinScanEntry scanEntry;
    uint32      i;

    /*
     * Look for an existing equivalent entry.
     *
     * Entries with non-null extra_data are never considered identical, since
     * we can't know exactly what the opclass might be doing with that.
     */
    if (extra_data == NULL)
    {
        for (i = 0; i < so->totalentries; i++)
        {
            GinScanEntry prevEntry = so->entries[i];

            if (prevEntry->extra_data == NULL &&
                prevEntry->isPartialMatch == isPartialMatch &&
                prevEntry->strategy == strategy &&
                prevEntry->searchMode == searchMode &&
                prevEntry->attnum == attnum &&
                ginCompareEntries(ginstate, attnum,
                                  prevEntry->queryKey,
                                  prevEntry->queryCategory,
                                  queryKey,
                                  queryCategory) == 0)
            {
                /* Successful match */
                return prevEntry;
            }
        }
    }

    /* Nope, create a new entry */
    scanEntry = (GinScanEntry) palloc(sizeof(GinScanEntryData));
    scanEntry->queryKey = queryKey;
    scanEntry->queryCategory = queryCategory;
    scanEntry->isPartialMatch = isPartialMatch;
    scanEntry->extra_data = extra_data;
    scanEntry->strategy = strategy;
    scanEntry->searchMode = searchMode;
    scanEntry->attnum = attnum;

    scanEntry->buffer = InvalidBuffer;
    ItemPointerSetMin(&scanEntry->curItem);
    scanEntry->matchBitmap = NULL;
    scanEntry->matchIterator = NULL;
    scanEntry->matchResult = NULL;
    scanEntry->list = NULL;
    scanEntry->nlist = 0;
    scanEntry->offset = InvalidOffsetNumber;
    scanEntry->isFinished = false;
    scanEntry->reduceResult = false;

    /* Add it to so's array */
    if (so->totalentries >= so->allocentries)
    {
        so->allocentries *= 2;
        so->entries = (GinScanEntry *)
            repalloc(so->entries, so->allocentries * sizeof(GinScanEntry));
    }
    so->entries[so->totalentries++] = scanEntry;

    return scanEntry;
}

static void ginFillScanKey ( GinScanOpaque  so,
OffsetNumber  attnum,
StrategyNumber  strategy,
int32  searchMode,
Datum  query,
uint32  nQueryValues,
Datum queryValues,
GinNullCategory queryCategories,
bool partial_matches,
Pointer extra_data 
) [static]

Definition at line 134 of file ginscan.c.

References GinScanKeyData::attnum, GinState::canPartialMatch, GinScanKeyData::curItem, GinScanKeyData::curItemMatches, elog, GinScanKeyData::entryRes, ERROR, GinScanKeyData::extra_data, GIN_SEARCH_MODE_ALL, GIN_SEARCH_MODE_DEFAULT, GIN_SEARCH_MODE_EVERYTHING, GIN_SEARCH_MODE_INCLUDE_EMPTY, ginFillScanEntry(), GinScanOpaqueData::ginstate, i, GinScanKeyData::isFinished, ItemPointerSetMin, GinScanOpaqueData::keys, GinScanKeyData::nentries, GinScanOpaqueData::nkeys, NULL, GinScanKeyData::nuserentries, palloc(), palloc0(), GinScanKeyData::query, GinScanKeyData::queryCategories, GinScanKeyData::queryValues, GinScanKeyData::recheckCurItem, GinScanKeyData::scanEntry, GinScanKeyData::searchMode, and GinScanKeyData::strategy.

Referenced by ginNewScanKey().

{
    GinScanKey  key = &(so->keys[so->nkeys++]);
    GinState   *ginstate = &so->ginstate;
    uint32      nUserQueryValues = nQueryValues;
    uint32      i;

    /* Non-default search modes add one "hidden" entry to each key */
    if (searchMode != GIN_SEARCH_MODE_DEFAULT)
        nQueryValues++;
    key->nentries = nQueryValues;
    key->nuserentries = nUserQueryValues;

    key->scanEntry = (GinScanEntry *) palloc(sizeof(GinScanEntry) * nQueryValues);
    key->entryRes = (bool *) palloc0(sizeof(bool) * nQueryValues);

    key->query = query;
    key->queryValues = queryValues;
    key->queryCategories = queryCategories;
    key->extra_data = extra_data;
    key->strategy = strategy;
    key->searchMode = searchMode;
    key->attnum = attnum;

    ItemPointerSetMin(&key->curItem);
    key->curItemMatches = false;
    key->recheckCurItem = false;
    key->isFinished = false;

    for (i = 0; i < nQueryValues; i++)
    {
        Datum       queryKey;
        GinNullCategory queryCategory;
        bool        isPartialMatch;
        Pointer     this_extra;

        if (i < nUserQueryValues)
        {
            /* set up normal entry using extractQueryFn's outputs */
            queryKey = queryValues[i];
            queryCategory = queryCategories[i];
            isPartialMatch =
                (ginstate->canPartialMatch[attnum - 1] && partial_matches)
                ? partial_matches[i] : false;
            this_extra = (extra_data) ? extra_data[i] : NULL;
        }
        else
        {
            /* set up hidden entry */
            queryKey = (Datum) 0;
            switch (searchMode)
            {
                case GIN_SEARCH_MODE_INCLUDE_EMPTY:
                    queryCategory = GIN_CAT_EMPTY_ITEM;
                    break;
                case GIN_SEARCH_MODE_ALL:
                    queryCategory = GIN_CAT_EMPTY_QUERY;
                    break;
                case GIN_SEARCH_MODE_EVERYTHING:
                    queryCategory = GIN_CAT_EMPTY_QUERY;
                    break;
                default:
                    elog(ERROR, "unexpected searchMode: %d", searchMode);
                    queryCategory = 0;  /* keep compiler quiet */
                    break;
            }
            isPartialMatch = false;
            this_extra = NULL;

            /*
             * We set the strategy to a fixed value so that ginFillScanEntry
             * can combine these entries for different scan keys.  This is
             * safe because the strategy value in the entry struct is only
             * used for partial-match cases.  It's OK to overwrite our local
             * variable here because this is the last loop iteration.
             */
            strategy = InvalidStrategy;
        }

        key->scanEntry[i] = ginFillScanEntry(so, attnum,
                                             strategy, searchMode,
                                             queryKey, queryCategory,
                                             isPartialMatch, this_extra);
    }
}

Datum ginmarkpos ( PG_FUNCTION_ARGS   ) 

Definition at line 445 of file ginscan.c.

References elog, ERROR, and PG_RETURN_VOID.

{
    elog(ERROR, "GIN does not support mark/restore");
    PG_RETURN_VOID();
}

void ginNewScanKey ( IndexScanDesc  scan  ) 

Definition at line 265 of file ginscan.c.

References GinScanOpaqueData::allocentries, DatumGetPointer, GinScanOpaqueData::entries, ereport, errcode(), errhint(), errmsg(), ERROR, GinState::extractQueryFn, FirstOffsetNumber, FunctionCall7Coll(), GIN_SEARCH_MODE_ALL, GIN_SEARCH_MODE_DEFAULT, GIN_SEARCH_MODE_EVERYTHING, ginFillScanKey(), ginGetStats(), GinScanOpaqueData::ginstate, GinStatsData::ginVersion, i, IndexScanDescData::indexRelation, InvalidStrategy, GinScanOpaqueData::isVoidRes, IndexScanDescData::keyData, GinScanOpaqueData::keys, Max, GinScanOpaqueData::nkeys, NULL, IndexScanDescData::numberOfKeys, IndexScanDescData::opaque, palloc(), palloc0(), pgstat_count_index_scan, PointerGetDatum, RelationGetRelationName, ScanKeyData::sk_argument, ScanKeyData::sk_attno, ScanKeyData::sk_flags, SK_ISNULL, ScanKeyData::sk_strategy, GinState::supportCollation, GinScanOpaqueData::totalentries, and UInt16GetDatum.

Referenced by gingetbitmap().

{
    ScanKey     scankey = scan->keyData;
    GinScanOpaque so = (GinScanOpaque) scan->opaque;
    int         i;
    bool        hasNullQuery = false;

    /* if no scan keys provided, allocate extra EVERYTHING GinScanKey */
    so->keys = (GinScanKey)
        palloc(Max(scan->numberOfKeys, 1) * sizeof(GinScanKeyData));
    so->nkeys = 0;

    /* initialize expansible array of GinScanEntry pointers */
    so->totalentries = 0;
    so->allocentries = 32;
    so->entries = (GinScanEntry *)
        palloc0(so->allocentries * sizeof(GinScanEntry));

    so->isVoidRes = false;

    for (i = 0; i < scan->numberOfKeys; i++)
    {
        ScanKey     skey = &scankey[i];
        Datum      *queryValues;
        int32       nQueryValues = 0;
        bool       *partial_matches = NULL;
        Pointer    *extra_data = NULL;
        bool       *nullFlags = NULL;
        int32       searchMode = GIN_SEARCH_MODE_DEFAULT;

        /*
         * We assume that GIN-indexable operators are strict, so a null query
         * argument means an unsatisfiable query.
         */
        if (skey->sk_flags & SK_ISNULL)
        {
            so->isVoidRes = true;
            break;
        }

        /* OK to call the extractQueryFn */
        queryValues = (Datum *)
            DatumGetPointer(FunctionCall7Coll(&so->ginstate.extractQueryFn[skey->sk_attno - 1],
                           so->ginstate.supportCollation[skey->sk_attno - 1],
                                              skey->sk_argument,
                                              PointerGetDatum(&nQueryValues),
                                           UInt16GetDatum(skey->sk_strategy),
                                           PointerGetDatum(&partial_matches),
                                              PointerGetDatum(&extra_data),
                                              PointerGetDatum(&nullFlags),
                                              PointerGetDatum(&searchMode)));

        /*
         * If bogus searchMode is returned, treat as GIN_SEARCH_MODE_ALL; note
         * in particular we don't allow extractQueryFn to select
         * GIN_SEARCH_MODE_EVERYTHING.
         */
        if (searchMode < GIN_SEARCH_MODE_DEFAULT ||
            searchMode > GIN_SEARCH_MODE_ALL)
            searchMode = GIN_SEARCH_MODE_ALL;

        /* Non-default modes require the index to have placeholders */
        if (searchMode != GIN_SEARCH_MODE_DEFAULT)
            hasNullQuery = true;

        /*
         * In default mode, no keys means an unsatisfiable query.
         */
        if (queryValues == NULL || nQueryValues <= 0)
        {
            if (searchMode == GIN_SEARCH_MODE_DEFAULT)
            {
                so->isVoidRes = true;
                break;
            }
            nQueryValues = 0;   /* ensure sane value */
        }

        /*
         * If the extractQueryFn didn't create a nullFlags array, create one,
         * assuming that everything's non-null.  Otherwise, run through the
         * array and make sure each value is exactly 0 or 1; this ensures
         * binary compatibility with the GinNullCategory representation. While
         * at it, detect whether any null keys are present.
         */
        if (nullFlags == NULL)
            nullFlags = (bool *) palloc0(nQueryValues * sizeof(bool));
        else
        {
            int32       j;

            for (j = 0; j < nQueryValues; j++)
            {
                if (nullFlags[j])
                {
                    nullFlags[j] = true;        /* not any other nonzero value */
                    hasNullQuery = true;
                }
            }
        }
        /* now we can use the nullFlags as category codes */

        ginFillScanKey(so, skey->sk_attno,
                       skey->sk_strategy, searchMode,
                       skey->sk_argument, nQueryValues,
                       queryValues, (GinNullCategory *) nullFlags,
                       partial_matches, extra_data);
    }

    /*
     * If there are no regular scan keys, generate an EVERYTHING scankey to
     * drive a full-index scan.
     */
    if (so->nkeys == 0 && !so->isVoidRes)
    {
        hasNullQuery = true;
        ginFillScanKey(so, FirstOffsetNumber,
                       InvalidStrategy, GIN_SEARCH_MODE_EVERYTHING,
                       (Datum) 0, 0,
                       NULL, NULL, NULL, NULL);
    }

    /*
     * If the index is version 0, it may be missing null and placeholder
     * entries, which would render searches for nulls and full-index scans
     * unreliable.  Throw an error if so.
     */
    if (hasNullQuery && !so->isVoidRes)
    {
        GinStatsData ginStats;

        ginGetStats(scan->indexRelation, &ginStats);
        if (ginStats.ginVersion < 1)
            ereport(ERROR,
                    (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                     errmsg("old GIN indexes do not support whole-index scans nor searches for nulls"),
                     errhint("To fix this, do REINDEX INDEX \"%s\".",
                             RelationGetRelationName(scan->indexRelation))));
    }

    pgstat_count_index_scan(scan->indexRelation);
}

Datum ginrescan ( PG_FUNCTION_ARGS   ) 

Definition at line 409 of file ginscan.c.

References freeScanKeys(), IndexScanDescData::keyData, memmove, IndexScanDescData::numberOfKeys, IndexScanDescData::opaque, PG_GETARG_POINTER, and PG_RETURN_VOID.

{
    IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
    ScanKey     scankey = (ScanKey) PG_GETARG_POINTER(1);

    /* remaining arguments are ignored */
    GinScanOpaque so = (GinScanOpaque) scan->opaque;

    freeScanKeys(so);

    if (scankey && scan->numberOfKeys > 0)
    {
        memmove(scan->keyData, scankey,
                scan->numberOfKeys * sizeof(ScanKeyData));
    }

    PG_RETURN_VOID();
}

Datum ginrestrpos ( PG_FUNCTION_ARGS   ) 

Definition at line 452 of file ginscan.c.

References elog, ERROR, and PG_RETURN_VOID.

{
    elog(ERROR, "GIN does not support mark/restore");
    PG_RETURN_VOID();
}