Header And Logo

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

Data Structures | Functions

ginutil.c File Reference

#include "postgres.h"
#include "access/gin_private.h"
#include "access/reloptions.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_type.h"
#include "miscadmin.h"
#include "storage/indexfsm.h"
#include "storage/lmgr.h"
Include dependency graph for ginutil.c:

Go to the source code of this file.

Data Structures

struct  keyEntryData
struct  cmpEntriesArg

Functions

void initGinState (GinState *state, Relation index)
OffsetNumber gintuple_get_attrnum (GinState *ginstate, IndexTuple tuple)
Datum gintuple_get_key (GinState *ginstate, IndexTuple tuple, GinNullCategory *category)
Buffer GinNewBuffer (Relation index)
void GinInitPage (Page page, uint32 f, Size pageSize)
void GinInitBuffer (Buffer b, uint32 f)
void GinInitMetabuffer (Buffer b)
int ginCompareEntries (GinState *ginstate, OffsetNumber attnum, Datum a, GinNullCategory categorya, Datum b, GinNullCategory categoryb)
int ginCompareAttEntries (GinState *ginstate, OffsetNumber attnuma, Datum a, GinNullCategory categorya, OffsetNumber attnumb, Datum b, GinNullCategory categoryb)
static int cmpEntries (const void *a, const void *b, void *arg)
DatumginExtractEntries (GinState *ginstate, OffsetNumber attnum, Datum value, bool isNull, int32 *nentries, GinNullCategory **categories)
Datum ginoptions (PG_FUNCTION_ARGS)
void ginGetStats (Relation index, GinStatsData *stats)
void ginUpdateStats (Relation index, const GinStatsData *stats)

Function Documentation

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

Definition at line 335 of file ginutil.c.

References cmpEntriesArg::cmpDatumFunc, cmpEntriesArg::collation, keyEntryData::datum, DatumGetInt32, FunctionCall2Coll(), cmpEntriesArg::haveDups, and keyEntryData::isnull.

Referenced by ginExtractEntries().

{
    const keyEntryData *aa = (const keyEntryData *) a;
    const keyEntryData *bb = (const keyEntryData *) b;
    cmpEntriesArg *data = (cmpEntriesArg *) arg;
    int         res;

    if (aa->isnull)
    {
        if (bb->isnull)
            res = 0;            /* NULL "=" NULL */
        else
            res = 1;            /* NULL ">" not-NULL */
    }
    else if (bb->isnull)
        res = -1;               /* not-NULL "<" NULL */
    else
        res = DatumGetInt32(FunctionCall2Coll(data->cmpDatumFunc,
                                              data->collation,
                                              aa->datum, bb->datum));

    /*
     * Detect if we have any duplicates.  If there are equal keys, qsort must
     * compare them at some point, else it wouldn't know whether one should go
     * before or after the other.
     */
    if (res == 0)
        data->haveDups = true;

    return res;
}

int ginCompareAttEntries ( GinState ginstate,
OffsetNumber  attnuma,
Datum  a,
GinNullCategory  categorya,
OffsetNumber  attnumb,
Datum  b,
GinNullCategory  categoryb 
)

Definition at line 302 of file ginutil.c.

References ginCompareEntries().

Referenced by cmpEntryAccumulator(), entryIsMoveRight(), entryLocateEntry(), and entryLocateLeafEntry().

{
    /* attribute number is the first sort key */
    if (attnuma != attnumb)
        return (attnuma < attnumb) ? -1 : 1;

    return ginCompareEntries(ginstate, attnuma, a, categorya, b, categoryb);
}

int ginCompareEntries ( GinState ginstate,
OffsetNumber  attnum,
Datum  a,
GinNullCategory  categorya,
Datum  b,
GinNullCategory  categoryb 
)

Definition at line 280 of file ginutil.c.

References GinState::compareFn, DatumGetInt32, FunctionCall2Coll(), GIN_CAT_NORM_KEY, and GinState::supportCollation.

Referenced by collectMatchBitmap(), collectMatchesForHeapRow(), ginCompareAttEntries(), and ginFillScanEntry().

{
    /* if not of same null category, sort by that first */
    if (categorya != categoryb)
        return (categorya < categoryb) ? -1 : 1;

    /* all null items in same category are equal */
    if (categorya != GIN_CAT_NORM_KEY)
        return 0;

    /* both not null, so safe to call the compareFn */
    return DatumGetInt32(FunctionCall2Coll(&ginstate->compareFn[attnum - 1],
                                      ginstate->supportCollation[attnum - 1],
                                           a, b));
}

Datum* ginExtractEntries ( GinState ginstate,
OffsetNumber  attnum,
Datum  value,
bool  isNull,
int32 nentries,
GinNullCategory **  categories 
)

Definition at line 375 of file ginutil.c.

References arg, cmpEntriesArg::cmpDatumFunc, cmpEntries(), cmpEntriesArg::collation, GinState::compareFn, keyEntryData::datum, DatumGetPointer, GinState::extractValueFn, FunctionCall3Coll(), cmpEntriesArg::haveDups, i, keyEntryData::isnull, NULL, palloc(), palloc0(), pfree(), PointerGetDatum, qsort_arg(), and GinState::supportCollation.

Referenced by ginHeapTupleBulkInsert(), ginHeapTupleFastCollect(), and ginHeapTupleInsert().

{
    Datum      *entries;
    bool       *nullFlags;
    int32       i;

    /*
     * We don't call the extractValueFn on a null item.  Instead generate a
     * placeholder.
     */
    if (isNull)
    {
        *nentries = 1;
        entries = (Datum *) palloc(sizeof(Datum));
        entries[0] = (Datum) 0;
        *categories = (GinNullCategory *) palloc(sizeof(GinNullCategory));
        (*categories)[0] = GIN_CAT_NULL_ITEM;
        return entries;
    }

    /* OK, call the opclass's extractValueFn */
    nullFlags = NULL;           /* in case extractValue doesn't set it */
    entries = (Datum *)
        DatumGetPointer(FunctionCall3Coll(&ginstate->extractValueFn[attnum - 1],
                                      ginstate->supportCollation[attnum - 1],
                                          value,
                                          PointerGetDatum(nentries),
                                          PointerGetDatum(&nullFlags)));

    /*
     * Generate a placeholder if the item contained no keys.
     */
    if (entries == NULL || *nentries <= 0)
    {
        *nentries = 1;
        entries = (Datum *) palloc(sizeof(Datum));
        entries[0] = (Datum) 0;
        *categories = (GinNullCategory *) palloc(sizeof(GinNullCategory));
        (*categories)[0] = GIN_CAT_EMPTY_ITEM;
        return entries;
    }

    /*
     * If the extractValueFn 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.
     */
    if (nullFlags == NULL)
        nullFlags = (bool *) palloc0(*nentries * sizeof(bool));
    else
    {
        for (i = 0; i < *nentries; i++)
            nullFlags[i] = (nullFlags[i] ? true : false);
    }
    /* now we can use the nullFlags as category codes */
    *categories = (GinNullCategory *) nullFlags;

    /*
     * If there's more than one key, sort and unique-ify.
     *
     * XXX Using qsort here is notationally painful, and the overhead is
     * pretty bad too.  For small numbers of keys it'd likely be better to use
     * a simple insertion sort.
     */
    if (*nentries > 1)
    {
        keyEntryData *keydata;
        cmpEntriesArg arg;

        keydata = (keyEntryData *) palloc(*nentries * sizeof(keyEntryData));
        for (i = 0; i < *nentries; i++)
        {
            keydata[i].datum = entries[i];
            keydata[i].isnull = nullFlags[i];
        }

        arg.cmpDatumFunc = &ginstate->compareFn[attnum - 1];
        arg.collation = ginstate->supportCollation[attnum - 1];
        arg.haveDups = false;
        qsort_arg(keydata, *nentries, sizeof(keyEntryData),
                  cmpEntries, (void *) &arg);

        if (arg.haveDups)
        {
            /* there are duplicates, must get rid of 'em */
            int32       j;

            entries[0] = keydata[0].datum;
            nullFlags[0] = keydata[0].isnull;
            j = 1;
            for (i = 1; i < *nentries; i++)
            {
                if (cmpEntries(&keydata[i - 1], &keydata[i], &arg) != 0)
                {
                    entries[j] = keydata[i].datum;
                    nullFlags[j] = keydata[i].isnull;
                    j++;
                }
            }
            *nentries = j;
        }
        else
        {
            /* easy, no duplicates */
            for (i = 0; i < *nentries; i++)
            {
                entries[i] = keydata[i].datum;
                nullFlags[i] = keydata[i].isnull;
            }
        }

        pfree(keydata);
    }

    return entries;
}

void ginGetStats ( Relation  index,
GinStatsData stats 
)
void GinInitBuffer ( Buffer  b,
uint32  f 
)
void GinInitMetabuffer ( Buffer  b  ) 
void GinInitPage ( Page  page,
uint32  f,
Size  pageSize 
)

Definition at line 237 of file ginutil.c.

References GinPageOpaqueData::flags, GinPageGetOpaque, PageInit(), and GinPageOpaqueData::rightlink.

Referenced by dataSplitPage(), entrySplitPage(), GinInitBuffer(), and GinInitMetabuffer().

{
    GinPageOpaque opaque;

    PageInit(page, pageSize, sizeof(GinPageOpaqueData));

    opaque = GinPageGetOpaque(page);
    memset(opaque, 0, sizeof(GinPageOpaqueData));
    opaque->flags = f;
    opaque->rightlink = InvalidBlockNumber;
}

Buffer GinNewBuffer ( Relation  index  ) 

Definition at line 186 of file ginutil.c.

References BufferGetPage, ConditionalLockBuffer(), ExclusiveLock, GetFreeIndexPage(), GIN_EXCLUSIVE, GIN_UNLOCK, GinPageIsDeleted, InvalidBlockNumber, LockBuffer(), LockRelationForExtension(), P_NEW, PageIsNew, ReadBuffer(), RELATION_IS_LOCAL, ReleaseBuffer(), and UnlockRelationForExtension().

Referenced by createPostingTree(), ginbuild(), ginInsertValue(), and makeSublist().

{
    Buffer      buffer;
    bool        needLock;

    /* First, try to get a page from FSM */
    for (;;)
    {
        BlockNumber blkno = GetFreeIndexPage(index);

        if (blkno == InvalidBlockNumber)
            break;

        buffer = ReadBuffer(index, blkno);

        /*
         * We have to guard against the possibility that someone else already
         * recycled this page; the buffer may be locked if so.
         */
        if (ConditionalLockBuffer(buffer))
        {
            Page        page = BufferGetPage(buffer);

            if (PageIsNew(page))
                return buffer;  /* OK to use, if never initialized */

            if (GinPageIsDeleted(page))
                return buffer;  /* OK to use */

            LockBuffer(buffer, GIN_UNLOCK);
        }

        /* Can't use it, so release buffer and try again */
        ReleaseBuffer(buffer);
    }

    /* Must extend the file */
    needLock = !RELATION_IS_LOCAL(index);
    if (needLock)
        LockRelationForExtension(index, ExclusiveLock);

    buffer = ReadBuffer(index, P_NEW);
    LockBuffer(buffer, GIN_EXCLUSIVE);

    if (needLock)
        UnlockRelationForExtension(index, ExclusiveLock);

    return buffer;
}

Datum ginoptions ( PG_FUNCTION_ARGS   ) 

Definition at line 496 of file ginutil.c.

References allocateReloptStruct(), fillRelOptions(), lengthof, offsetof, parseRelOptions(), pfree(), PG_GETARG_BOOL, PG_GETARG_DATUM, PG_RETURN_BYTEA_P, PG_RETURN_NULL, and RELOPT_KIND_GIN.

{
    Datum       reloptions = PG_GETARG_DATUM(0);
    bool        validate = PG_GETARG_BOOL(1);
    relopt_value *options;
    GinOptions *rdopts;
    int         numoptions;
    static const relopt_parse_elt tab[] = {
        {"fastupdate", RELOPT_TYPE_BOOL, offsetof(GinOptions, useFastUpdate)}
    };

    options = parseRelOptions(reloptions, validate, RELOPT_KIND_GIN,
                              &numoptions);

    /* if none set, we're done */
    if (numoptions == 0)
        PG_RETURN_NULL();

    rdopts = allocateReloptStruct(sizeof(GinOptions), options, numoptions);

    fillRelOptions((void *) rdopts, sizeof(GinOptions), options, numoptions,
                   validate, tab, lengthof(tab));

    pfree(options);

    PG_RETURN_BYTEA_P(rdopts);
}

OffsetNumber gintuple_get_attrnum ( GinState ginstate,
IndexTuple  tuple 
)

Definition at line 112 of file ginutil.c.

References Assert, DatumGetUInt16, FirstOffsetNumber, index_getattr, GinState::oneCol, and GinState::tupdesc.

Referenced by addItemPointersToLeafTuple(), collectMatchBitmap(), collectMatchesForHeapRow(), entryIsMoveRight(), entryLocateEntry(), entryLocateLeafEntry(), gintuple_get_key(), ginVacuumEntryPage(), matchPartialInPendingList(), and processPendingPage().

{
    OffsetNumber colN;

    if (ginstate->oneCol)
    {
        /* column number is not stored explicitly */
        colN = FirstOffsetNumber;
    }
    else
    {
        Datum       res;
        bool        isnull;

        /*
         * First attribute is always int16, so we can safely use any tuple
         * descriptor to obtain first attribute of tuple
         */
        res = index_getattr(tuple, FirstOffsetNumber, ginstate->tupdesc[0],
                            &isnull);
        Assert(!isnull);

        colN = DatumGetUInt16(res);
        Assert(colN >= FirstOffsetNumber && colN <= ginstate->origTupdesc->natts);
    }

    return colN;
}

Datum gintuple_get_key ( GinState ginstate,
IndexTuple  tuple,
GinNullCategory category 
)

Definition at line 145 of file ginutil.c.

References FirstOffsetNumber, GinGetNullCategory, gintuple_get_attrnum(), index_getattr, OffsetNumberNext, GinState::oneCol, GinState::origTupdesc, and GinState::tupdesc.

Referenced by addItemPointersToLeafTuple(), collectMatchBitmap(), collectMatchesForHeapRow(), entryIsMoveRight(), entryLocateEntry(), entryLocateLeafEntry(), ginVacuumEntryPage(), matchPartialInPendingList(), and processPendingPage().

{
    Datum       res;
    bool        isnull;

    if (ginstate->oneCol)
    {
        /*
         * Single column index doesn't store attribute numbers in tuples
         */
        res = index_getattr(tuple, FirstOffsetNumber, ginstate->origTupdesc,
                            &isnull);
    }
    else
    {
        /*
         * Since the datum type depends on which index column it's from, we
         * must be careful to use the right tuple descriptor here.
         */
        OffsetNumber colN = gintuple_get_attrnum(ginstate, tuple);

        res = index_getattr(tuple, OffsetNumberNext(FirstOffsetNumber),
                            ginstate->tupdesc[colN - 1],
                            &isnull);
    }

    if (isnull)
        *category = GinGetNullCategory(tuple, ginstate);
    else
        *category = GIN_CAT_NORM_KEY;

    return res;
}

void ginUpdateStats ( Relation  index,
const GinStatsData stats 
)

Definition at line 558 of file ginutil.c.

References XLogRecData::buffer, BufferGetPage, XLogRecData::data, END_CRIT_SECTION, GIN_EXCLUSIVE, GIN_METAPAGE_BLKNO, GinPageGetMeta, XLogRecData::len, LockBuffer(), MarkBufferDirty(), ginxlogUpdateMeta::metadata, GinStatsData::nDataPages, GinMetaPageData::nDataPages, GinStatsData::nEntries, GinMetaPageData::nEntries, GinStatsData::nEntryPages, GinMetaPageData::nEntryPages, ginxlogUpdateMeta::newRightlink, XLogRecData::next, ginxlogUpdateMeta::node, GinStatsData::nTotalPages, GinMetaPageData::nTotalPages, ginxlogUpdateMeta::ntuples, PageSetLSN, ginxlogUpdateMeta::prevTail, RelationData::rd_node, ReadBuffer(), RelationNeedsWAL, START_CRIT_SECTION, UnlockReleaseBuffer(), XLOG_GIN_UPDATE_META_PAGE, and XLogInsert().

Referenced by ginbuild(), and ginvacuumcleanup().

{
    Buffer      metabuffer;
    Page        metapage;
    GinMetaPageData *metadata;

    metabuffer = ReadBuffer(index, GIN_METAPAGE_BLKNO);
    LockBuffer(metabuffer, GIN_EXCLUSIVE);
    metapage = BufferGetPage(metabuffer);
    metadata = GinPageGetMeta(metapage);

    START_CRIT_SECTION();

    metadata->nTotalPages = stats->nTotalPages;
    metadata->nEntryPages = stats->nEntryPages;
    metadata->nDataPages = stats->nDataPages;
    metadata->nEntries = stats->nEntries;

    MarkBufferDirty(metabuffer);

    if (RelationNeedsWAL(index))
    {
        XLogRecPtr  recptr;
        ginxlogUpdateMeta data;
        XLogRecData rdata;

        data.node = index->rd_node;
        data.ntuples = 0;
        data.newRightlink = data.prevTail = InvalidBlockNumber;
        memcpy(&data.metadata, metadata, sizeof(GinMetaPageData));

        rdata.buffer = InvalidBuffer;
        rdata.data = (char *) &data;
        rdata.len = sizeof(ginxlogUpdateMeta);
        rdata.next = NULL;

        recptr = XLogInsert(RM_GIN_ID, XLOG_GIN_UPDATE_META_PAGE, &rdata);
        PageSetLSN(metapage, recptr);
    }

    UnlockReleaseBuffer(metabuffer);

    END_CRIT_SECTION();
}

void initGinState ( GinState state,
Relation  index 
)

Definition at line 32 of file ginutil.c.

References tupleDesc::attrs, GinState::canPartialMatch, GinState::compareFn, GinState::comparePartialFn, GinState::consistentFn, CreateTemplateTupleDesc(), CurrentMemoryContext, GinState::extractQueryFn, GinState::extractValueFn, fmgr_info_copy(), GIN_COMPARE_PARTIAL_PROC, GIN_COMPARE_PROC, GIN_CONSISTENT_PROC, GIN_EXTRACTQUERY_PROC, GIN_EXTRACTVALUE_PROC, i, GinState::index, index_getprocid(), index_getprocinfo(), INT2OID, InvalidOid, MemSet, tupleDesc::natts, NULL, OidIsValid, GinState::oneCol, GinState::origTupdesc, RelationData::rd_indcollation, RelationGetDescr, GinState::supportCollation, GinState::tupdesc, TupleDescInitEntry(), and TupleDescInitEntryCollation().

Referenced by ginbeginscan(), ginbuild(), ginbulkdelete(), gininsert(), and ginvacuumcleanup().

{
    TupleDesc   origTupdesc = RelationGetDescr(index);
    int         i;

    MemSet(state, 0, sizeof(GinState));

    state->index = index;
    state->oneCol = (origTupdesc->natts == 1) ? true : false;
    state->origTupdesc = origTupdesc;

    for (i = 0; i < origTupdesc->natts; i++)
    {
        if (state->oneCol)
            state->tupdesc[i] = state->origTupdesc;
        else
        {
            state->tupdesc[i] = CreateTemplateTupleDesc(2, false);

            TupleDescInitEntry(state->tupdesc[i], (AttrNumber) 1, NULL,
                               INT2OID, -1, 0);
            TupleDescInitEntry(state->tupdesc[i], (AttrNumber) 2, NULL,
                               origTupdesc->attrs[i]->atttypid,
                               origTupdesc->attrs[i]->atttypmod,
                               origTupdesc->attrs[i]->attndims);
            TupleDescInitEntryCollation(state->tupdesc[i], (AttrNumber) 2,
                                        origTupdesc->attrs[i]->attcollation);
        }

        fmgr_info_copy(&(state->compareFn[i]),
                       index_getprocinfo(index, i + 1, GIN_COMPARE_PROC),
                       CurrentMemoryContext);
        fmgr_info_copy(&(state->extractValueFn[i]),
                       index_getprocinfo(index, i + 1, GIN_EXTRACTVALUE_PROC),
                       CurrentMemoryContext);
        fmgr_info_copy(&(state->extractQueryFn[i]),
                       index_getprocinfo(index, i + 1, GIN_EXTRACTQUERY_PROC),
                       CurrentMemoryContext);
        fmgr_info_copy(&(state->consistentFn[i]),
                       index_getprocinfo(index, i + 1, GIN_CONSISTENT_PROC),
                       CurrentMemoryContext);

        /*
         * Check opclass capability to do partial match.
         */
        if (index_getprocid(index, i + 1, GIN_COMPARE_PARTIAL_PROC) != InvalidOid)
        {
            fmgr_info_copy(&(state->comparePartialFn[i]),
                   index_getprocinfo(index, i + 1, GIN_COMPARE_PARTIAL_PROC),
                           CurrentMemoryContext);
            state->canPartialMatch[i] = true;
        }
        else
        {
            state->canPartialMatch[i] = false;
        }

        /*
         * If the index column has a specified collation, we should honor that
         * while doing comparisons.  However, we may have a collatable storage
         * type for a noncollatable indexed data type (for instance, hstore
         * uses text index entries).  If there's no index collation then
         * specify default collation in case the support functions need
         * collation.  This is harmless if the support functions don't care
         * about collation, so we just do it unconditionally.  (We could
         * alternatively call get_typcollation, but that seems like expensive
         * overkill --- there aren't going to be any cases where a GIN storage
         * type has a nondefault collation.)
         */
        if (OidIsValid(index->rd_indcollation[i]))
            state->supportCollation[i] = index->rd_indcollation[i];
        else
            state->supportCollation[i] = DEFAULT_COLLATION_OID;
    }
}