Header And Logo

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

Defines | Functions

spgutils.c File Reference

#include "postgres.h"
#include "access/genam.h"
#include "access/reloptions.h"
#include "access/spgist_private.h"
#include "access/transam.h"
#include "access/xact.h"
#include "storage/bufmgr.h"
#include "storage/indexfsm.h"
#include "storage/lmgr.h"
#include "utils/lsyscache.h"
Include dependency graph for spgutils.c:

Go to the source code of this file.

Defines

#define GET_LUP(c, f)   (&(c)->lastUsedPages.cachedPage[((unsigned int) (f)) % SPGIST_CACHED_PAGES])

Functions

static void fillTypeDesc (SpGistTypeDesc *desc, Oid type)
SpGistCachespgGetCache (Relation index)
void initSpGistState (SpGistState *state, Relation index)
Buffer SpGistNewBuffer (Relation index)
void SpGistUpdateMetaPage (Relation index)
static Buffer allocNewBuffer (Relation index, int flags)
Buffer SpGistGetBuffer (Relation index, int flags, int needSpace, bool *isNew)
void SpGistSetLastUsedPage (Relation index, Buffer buffer)
void SpGistInitPage (Page page, uint16 f)
void SpGistInitBuffer (Buffer b, uint16 f)
void SpGistInitMetapage (Page page)
Datum spgoptions (PG_FUNCTION_ARGS)
unsigned int SpGistGetTypeSize (SpGistTypeDesc *att, Datum datum)
static void memcpyDatum (void *target, SpGistTypeDesc *att, Datum datum)
SpGistLeafTuple spgFormLeafTuple (SpGistState *state, ItemPointer heapPtr, Datum datum, bool isnull)
SpGistNodeTuple spgFormNodeTuple (SpGistState *state, Datum label, bool isnull)
SpGistInnerTuple spgFormInnerTuple (SpGistState *state, bool hasPrefix, Datum prefix, int nNodes, SpGistNodeTuple *nodes)
SpGistDeadTuple spgFormDeadTuple (SpGistState *state, int tupstate, BlockNumber blkno, OffsetNumber offnum)
DatumspgExtractNodeLabels (SpGistState *state, SpGistInnerTuple innerTuple)
OffsetNumber SpGistPageAddNewItem (SpGistState *state, Page page, Item item, Size size, OffsetNumber *startOffset, bool errorOK)

Define Documentation

#define GET_LUP (   c,
  f 
)    (&(c)->lastUsedPages.cachedPage[((unsigned int) (f)) % SPGIST_CACHED_PAGES])

Definition at line 230 of file spgutils.c.

Referenced by SpGistGetBuffer(), and SpGistSetLastUsedPage().


Function Documentation

static Buffer allocNewBuffer ( Relation  index,
int  flags 
) [static]

Definition at line 253 of file spgutils.c.

References SpGistLastUsedPage::blkno, BufferGetBlockNumber(), BufferGetPage, SpGistLUPCache::cachedPage, SpGistLastUsedPage::freeSpace, GBUF_INNER_PARITY, GBUF_PARITY_MASK, GBUF_REQ_LEAF, GBUF_REQ_NULLS, SpGistCache::lastUsedPages, PageGetExactFreeSpace(), spgGetCache(), SPGIST_LEAF, SPGIST_NULLS, SpGistInitBuffer(), SpGistNewBuffer(), and UnlockReleaseBuffer().

Referenced by SpGistGetBuffer().

{
    SpGistCache *cache = spgGetCache(index);
    uint16      pageflags = 0;

    if (GBUF_REQ_LEAF(flags))
        pageflags |= SPGIST_LEAF;
    if (GBUF_REQ_NULLS(flags))
        pageflags |= SPGIST_NULLS;

    for (;;)
    {
        Buffer      buffer;

        buffer = SpGistNewBuffer(index);
        SpGistInitBuffer(buffer, pageflags);

        if (pageflags & SPGIST_LEAF)
        {
            /* Leaf pages have no parity concerns, so just use it */
            return buffer;
        }
        else
        {
            BlockNumber blkno = BufferGetBlockNumber(buffer);
            int         blkFlags = GBUF_INNER_PARITY(blkno);

            if ((flags & GBUF_PARITY_MASK) == blkFlags)
            {
                /* Page has right parity, use it */
                return buffer;
            }
            else
            {
                /* Page has wrong parity, record it in cache and try again */
                if (pageflags & SPGIST_NULLS)
                    blkFlags |= GBUF_NULLS;
                cache->lastUsedPages.cachedPage[blkFlags].blkno = blkno;
                cache->lastUsedPages.cachedPage[blkFlags].freeSpace =
                    PageGetExactFreeSpace(BufferGetPage(buffer));
                UnlockReleaseBuffer(buffer);
            }
        }
    }
}

static void fillTypeDesc ( SpGistTypeDesc desc,
Oid  type 
) [static]

Definition at line 31 of file spgutils.c.

References SpGistTypeDesc::attbyval, SpGistTypeDesc::attlen, get_typlenbyval(), and SpGistTypeDesc::type.

Referenced by spgGetCache().

{
    desc->type = type;
    get_typlenbyval(type, &desc->attlen, &desc->attbyval);
}

void initSpGistState ( SpGistState state,
Relation  index 
)

Definition at line 108 of file spgutils.c.

References SpGistCache::attLabelType, SpGistState::attLabelType, SpGistCache::attPrefixType, SpGistState::attPrefixType, SpGistCache::attType, SpGistState::attType, SpGistCache::config, SpGistState::config, SpGistState::deadTupleStorage, GetTopTransactionIdIfAny(), SpGistState::isBuild, SpGistState::myXid, palloc0(), SGDTSIZE, and spgGetCache().

Referenced by spgbeginscan(), spgbuild(), spginsert(), and spgvacuumscan().

{
    SpGistCache *cache;

    /* Get cached static information about index */
    cache = spgGetCache(index);

    state->config = cache->config;
    state->attType = cache->attType;
    state->attPrefixType = cache->attPrefixType;
    state->attLabelType = cache->attLabelType;

    /* Make workspace for constructing dead tuples */
    state->deadTupleStorage = palloc0(SGDTSIZE);

    /* Set XID to use in redirection tuples */
    state->myXid = GetTopTransactionIdIfAny();

    /* Assume we're not in an index build (spgbuild will override) */
    state->isBuild = false;
}

static void memcpyDatum ( void *  target,
SpGistTypeDesc att,
Datum  datum 
) [static]

Definition at line 531 of file spgutils.c.

References SpGistTypeDesc::attbyval, SpGistTypeDesc::attlen, DatumGetPointer, and VARSIZE_ANY.

Referenced by spgFormInnerTuple(), spgFormLeafTuple(), and spgFormNodeTuple().

{
    unsigned int size;

    if (att->attbyval)
    {
        memcpy(target, &datum, sizeof(Datum));
    }
    else
    {
        size = (att->attlen > 0) ? att->attlen : VARSIZE_ANY(datum);
        memcpy(target, DatumGetPointer(datum), size);
    }
}

Datum* spgExtractNodeLabels ( SpGistState state,
SpGistInnerTuple  innerTuple 
)

Definition at line 743 of file spgutils.c.

References elog, ERROR, i, IndexTupleHasNulls, SpGistInnerTupleData::nNodes, palloc(), SGITITERATE, SGITNODEPTR, and SGNTDATUM.

Referenced by spgdoinsert(), and spgWalk().

{
    Datum      *nodeLabels;
    int         i;
    SpGistNodeTuple node;

    /* Either all the labels must be NULL, or none. */
    node = SGITNODEPTR(innerTuple);
    if (IndexTupleHasNulls(node))
    {
        SGITITERATE(innerTuple, i, node)
        {
            if (!IndexTupleHasNulls(node))
                elog(ERROR, "some but not all node labels are null in SPGiST inner tuple");
        }
        /* They're all null, so just return NULL */
        return NULL;
    }
    else
    {
        nodeLabels = (Datum *) palloc(sizeof(Datum) * innerTuple->nNodes);
        SGITITERATE(innerTuple, i, node)
        {
            if (IndexTupleHasNulls(node))
                elog(ERROR, "some but not all node labels are null in SPGiST inner tuple");
            nodeLabels[i] = SGNTDATUM(node, state);
        }
        return nodeLabels;
    }
}

SpGistDeadTuple spgFormDeadTuple ( SpGistState state,
int  tupstate,
BlockNumber  blkno,
OffsetNumber  offnum 
)
SpGistInnerTuple spgFormInnerTuple ( SpGistState state,
bool  hasPrefix,
Datum  prefix,
int  nNodes,
SpGistNodeTuple nodes 
)

Definition at line 630 of file spgutils.c.

References SpGistState::attPrefixType, elog, ereport, errcode(), errhint(), errmsg(), ERROR, i, IndexTupleSize, memcpyDatum(), SpGistInnerTupleData::nNodes, palloc0(), SpGistInnerTupleData::prefixSize, SGDTSIZE, SGITDATAPTR, SGITHDRSZ, SGITMAXNNODES, SGITMAXPREFIXSIZE, SGITMAXSIZE, SGITNODEPTR, SpGistInnerTupleData::size, SPGIST_PAGE_CAPACITY, and SpGistGetTypeSize().

Referenced by addNode(), doPickSplit(), and spgSplitNodeAction().

{
    SpGistInnerTuple tup;
    unsigned int size;
    unsigned int prefixSize;
    int         i;
    char       *ptr;

    /* Compute size needed */
    if (hasPrefix)
        prefixSize = SpGistGetTypeSize(&state->attPrefixType, prefix);
    else
        prefixSize = 0;

    size = SGITHDRSZ + prefixSize;

    /* Note: we rely on node tuple sizes to be maxaligned already */
    for (i = 0; i < nNodes; i++)
        size += IndexTupleSize(nodes[i]);

    /*
     * Ensure that we can replace the tuple with a dead tuple later.  This
     * test is unnecessary given current tuple layouts, but let's be safe.
     */
    if (size < SGDTSIZE)
        size = SGDTSIZE;

    /*
     * Inner tuple should be small enough to fit on a page
     */
    if (size > SPGIST_PAGE_CAPACITY - sizeof(ItemIdData))
        ereport(ERROR,
                (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
                 errmsg("SP-GiST inner tuple size %lu exceeds maximum %lu",
                        (unsigned long) size,
                (unsigned long) (SPGIST_PAGE_CAPACITY - sizeof(ItemIdData))),
            errhint("Values larger than a buffer page cannot be indexed.")));

    /*
     * Check for overflow of header fields --- probably can't fail if the
     * above succeeded, but let's be paranoid
     */
    if (size > SGITMAXSIZE ||
        prefixSize > SGITMAXPREFIXSIZE ||
        nNodes > SGITMAXNNODES)
        elog(ERROR, "SPGiST inner tuple header field is too small");

    /* OK, form the tuple */
    tup = (SpGistInnerTuple) palloc0(size);

    tup->nNodes = nNodes;
    tup->prefixSize = prefixSize;
    tup->size = size;

    if (hasPrefix)
        memcpyDatum(SGITDATAPTR(tup), &state->attPrefixType, prefix);

    ptr = (char *) SGITNODEPTR(tup);

    for (i = 0; i < nNodes; i++)
    {
        SpGistNodeTuple node = nodes[i];

        memcpy(ptr, node, IndexTupleSize(node));
        ptr += IndexTupleSize(node);
    }

    return tup;
}

SpGistLeafTuple spgFormLeafTuple ( SpGistState state,
ItemPointer  heapPtr,
Datum  datum,
bool  isnull 
)

Definition at line 550 of file spgutils.c.

References SpGistState::attType, SpGistLeafTupleData::heapPtr, memcpyDatum(), SpGistLeafTupleData::nextOffset, palloc0(), SGDTSIZE, SGLTDATAPTR, SpGistLeafTupleData::size, and SpGistGetTypeSize().

Referenced by doPickSplit(), and spgdoinsert().

{
    SpGistLeafTuple tup;
    unsigned int size;

    /* compute space needed (note result is already maxaligned) */
    size = SGLTHDRSZ;
    if (!isnull)
        size += SpGistGetTypeSize(&state->attType, datum);

    /*
     * Ensure that we can replace the tuple with a dead tuple later.  This
     * test is unnecessary when !isnull, but let's be safe.
     */
    if (size < SGDTSIZE)
        size = SGDTSIZE;

    /* OK, form the tuple */
    tup = (SpGistLeafTuple) palloc0(size);

    tup->size = size;
    tup->nextOffset = InvalidOffsetNumber;
    tup->heapPtr = *heapPtr;
    if (!isnull)
        memcpyDatum(SGLTDATAPTR(tup), &state->attType, datum);

    return tup;
}

SpGistNodeTuple spgFormNodeTuple ( SpGistState state,
Datum  label,
bool  isnull 
)

Definition at line 587 of file spgutils.c.

References SpGistState::attLabelType, ereport, errcode(), errmsg(), ERROR, INDEX_SIZE_MASK, ItemPointerSetInvalid, memcpyDatum(), palloc0(), SGNTDATAPTR, SpGistGetTypeSize(), IndexTupleData::t_info, and IndexTupleData::t_tid.

Referenced by addNode(), doPickSplit(), and spgSplitNodeAction().

{
    SpGistNodeTuple tup;
    unsigned int size;
    unsigned short infomask = 0;

    /* compute space needed (note result is already maxaligned) */
    size = SGNTHDRSZ;
    if (!isnull)
        size += SpGistGetTypeSize(&state->attLabelType, label);

    /*
     * Here we make sure that the size will fit in the field reserved for it
     * in t_info.
     */
    if ((size & INDEX_SIZE_MASK) != size)
        ereport(ERROR,
                (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
                 errmsg("index row requires %lu bytes, maximum size is %lu",
                        (unsigned long) size,
                        (unsigned long) INDEX_SIZE_MASK)));

    tup = (SpGistNodeTuple) palloc0(size);

    if (isnull)
        infomask |= INDEX_NULL_MASK;
    /* we don't bother setting the INDEX_VAR_MASK bit */
    infomask |= size;
    tup->t_info = infomask;

    /* The TID field will be filled in later */
    ItemPointerSetInvalid(&tup->t_tid);

    if (!isnull)
        memcpyDatum(SGNTDATAPTR(tup), &state->attLabelType, label);

    return tup;
}

SpGistCache* spgGetCache ( Relation  index  ) 

Definition at line 42 of file spgutils.c.

References Assert, SpGistCache::attLabelType, SpGistCache::attPrefixType, tupleDesc::attrs, SpGistCache::attType, spgConfigIn::attType, BUFFER_LOCK_SHARE, BufferGetPage, SpGistCache::config, elog, ERROR, fillTypeDesc(), FunctionCall2Coll(), index_getprocinfo(), spgConfigOut::labelType, SpGistMetaPageData::lastUsedPages, SpGistCache::lastUsedPages, LockBuffer(), SpGistMetaPageData::magicNumber, MemoryContextAllocZero(), tupleDesc::natts, NULL, PointerGetDatum, spgConfigOut::prefixType, RelationData::rd_amcache, RelationData::rd_att, RelationData::rd_indcollation, RelationData::rd_indexcxt, ReadBuffer(), RelationGetRelationName, SPGIST_CONFIG_PROC, SPGIST_MAGIC_NUMBER, SPGIST_METAPAGE_BLKNO, SpGistPageGetMeta, and UnlockReleaseBuffer().

Referenced by allocNewBuffer(), initSpGistState(), spgcanreturn(), SpGistGetBuffer(), and SpGistSetLastUsedPage().

{
    SpGistCache *cache;

    if (index->rd_amcache == NULL)
    {
        Oid         atttype;
        spgConfigIn in;
        FmgrInfo   *procinfo;
        Buffer      metabuffer;
        SpGistMetaPageData *metadata;

        cache = MemoryContextAllocZero(index->rd_indexcxt,
                                       sizeof(SpGistCache));

        /* SPGiST doesn't support multi-column indexes */
        Assert(index->rd_att->natts == 1);

        /*
         * Get the actual data type of the indexed column from the index
         * tupdesc.  We pass this to the opclass config function so that
         * polymorphic opclasses are possible.
         */
        atttype = index->rd_att->attrs[0]->atttypid;

        /* Call the config function to get config info for the opclass */
        in.attType = atttype;

        procinfo = index_getprocinfo(index, 1, SPGIST_CONFIG_PROC);
        FunctionCall2Coll(procinfo,
                          index->rd_indcollation[0],
                          PointerGetDatum(&in),
                          PointerGetDatum(&cache->config));

        /* Get the information we need about each relevant datatype */
        fillTypeDesc(&cache->attType, atttype);
        fillTypeDesc(&cache->attPrefixType, cache->config.prefixType);
        fillTypeDesc(&cache->attLabelType, cache->config.labelType);

        /* Last, get the lastUsedPages data from the metapage */
        metabuffer = ReadBuffer(index, SPGIST_METAPAGE_BLKNO);
        LockBuffer(metabuffer, BUFFER_LOCK_SHARE);

        metadata = SpGistPageGetMeta(BufferGetPage(metabuffer));

        if (metadata->magicNumber != SPGIST_MAGIC_NUMBER)
            elog(ERROR, "index \"%s\" is not an SP-GiST index",
                 RelationGetRelationName(index));

        cache->lastUsedPages = metadata->lastUsedPages;

        UnlockReleaseBuffer(metabuffer);

        index->rd_amcache = (void *) cache;
    }
    else
    {
        /* assume it's up to date */
        cache = (SpGistCache *) index->rd_amcache;
    }

    return cache;
}

Buffer SpGistGetBuffer ( Relation  index,
int  flags,
int  needSpace,
bool isNew 
)

Definition at line 309 of file spgutils.c.

References allocNewBuffer(), Assert, SpGistLastUsedPage::blkno, BufferGetPage, ConditionalLockBuffer(), elog, ERROR, SpGistLastUsedPage::freeSpace, GBUF_REQ_LEAF, GBUF_REQ_NULLS, GET_LUP, InvalidBlockNumber, Min, PageGetExactFreeSpace(), PageIsEmpty, PageIsNew, ReadBuffer(), RelationGetTargetPageFreeSpace, ReleaseBuffer(), spgGetCache(), SPGIST_DEFAULT_FILLFACTOR, SPGIST_PAGE_CAPACITY, SpGistBlockIsFixed, SpGistInitBuffer(), SpGistPageIsDeleted, SpGistPageIsLeaf, SpGistPageStoresNulls, and UnlockReleaseBuffer().

Referenced by doPickSplit(), moveLeafs(), spgAddNodeAction(), spgdoinsert(), and spgSplitNodeAction().

{
    SpGistCache *cache = spgGetCache(index);
    SpGistLastUsedPage *lup;

    /* Bail out if even an empty page wouldn't meet the demand */
    if (needSpace > SPGIST_PAGE_CAPACITY)
        elog(ERROR, "desired SPGiST tuple size is too big");

    /*
     * If possible, increase the space request to include relation's
     * fillfactor.  This ensures that when we add unrelated tuples to a page,
     * we try to keep 100-fillfactor% available for adding tuples that are
     * related to the ones already on it.  But fillfactor mustn't cause an
     * error for requests that would otherwise be legal.
     */
    needSpace += RelationGetTargetPageFreeSpace(index,
                                                SPGIST_DEFAULT_FILLFACTOR);
    needSpace = Min(needSpace, SPGIST_PAGE_CAPACITY);

    /* Get the cache entry for this flags setting */
    lup = GET_LUP(cache, flags);

    /* If we have nothing cached, just turn it over to allocNewBuffer */
    if (lup->blkno == InvalidBlockNumber)
    {
        *isNew = true;
        return allocNewBuffer(index, flags);
    }

    /* fixed pages should never be in cache */
    Assert(!SpGistBlockIsFixed(lup->blkno));

    /* If cached freeSpace isn't enough, don't bother looking at the page */
    if (lup->freeSpace >= needSpace)
    {
        Buffer      buffer;
        Page        page;

        buffer = ReadBuffer(index, lup->blkno);

        if (!ConditionalLockBuffer(buffer))
        {
            /*
             * buffer is locked by another process, so return a new buffer
             */
            ReleaseBuffer(buffer);
            *isNew = true;
            return allocNewBuffer(index, flags);
        }

        page = BufferGetPage(buffer);

        if (PageIsNew(page) || SpGistPageIsDeleted(page) || PageIsEmpty(page))
        {
            /* OK to initialize the page */
            uint16      pageflags = 0;

            if (GBUF_REQ_LEAF(flags))
                pageflags |= SPGIST_LEAF;
            if (GBUF_REQ_NULLS(flags))
                pageflags |= SPGIST_NULLS;
            SpGistInitBuffer(buffer, pageflags);
            lup->freeSpace = PageGetExactFreeSpace(page) - needSpace;
            *isNew = true;
            return buffer;
        }

        /*
         * Check that page is of right type and has enough space.  We must
         * recheck this since our cache isn't necessarily up to date.
         */
        if ((GBUF_REQ_LEAF(flags) ? SpGistPageIsLeaf(page) : !SpGistPageIsLeaf(page)) &&
            (GBUF_REQ_NULLS(flags) ? SpGistPageStoresNulls(page) : !SpGistPageStoresNulls(page)))
        {
            int         freeSpace = PageGetExactFreeSpace(page);

            if (freeSpace >= needSpace)
            {
                /* Success, update freespace info and return the buffer */
                lup->freeSpace = freeSpace - needSpace;
                *isNew = false;
                return buffer;
            }
        }

        /*
         * fallback to allocation of new buffer
         */
        UnlockReleaseBuffer(buffer);
    }

    /* No success with cache, so return a new buffer */
    *isNew = true;
    return allocNewBuffer(index, flags);
}

unsigned int SpGistGetTypeSize ( SpGistTypeDesc att,
Datum  datum 
)

Definition at line 513 of file spgutils.c.

References SpGistTypeDesc::attbyval, SpGistTypeDesc::attlen, MAXALIGN, and VARSIZE_ANY.

Referenced by spgdoinsert(), spgFormInnerTuple(), spgFormLeafTuple(), and spgFormNodeTuple().

{
    unsigned int size;

    if (att->attbyval)
        size = sizeof(Datum);
    else if (att->attlen > 0)
        size = att->attlen;
    else
        size = VARSIZE_ANY(datum);

    return MAXALIGN(size);
}

void SpGistInitBuffer ( Buffer  b,
uint16  f 
)
void SpGistInitMetapage ( Page  page  ) 

Definition at line 474 of file spgutils.c.

References SpGistLastUsedPage::blkno, SpGistLUPCache::cachedPage, i, InvalidBlockNumber, SpGistMetaPageData::lastUsedPages, SpGistMetaPageData::magicNumber, SPGIST_META, SpGistInitPage(), and SpGistPageGetMeta.

Referenced by spgbuild(), spgbuildempty(), and spgRedoCreateIndex().

{
    SpGistMetaPageData *metadata;
    int         i;

    SpGistInitPage(page, SPGIST_META);
    metadata = SpGistPageGetMeta(page);
    memset(metadata, 0, sizeof(SpGistMetaPageData));
    metadata->magicNumber = SPGIST_MAGIC_NUMBER;

    /* initialize last-used-page cache to empty */
    for (i = 0; i < SPGIST_CACHED_PAGES; i++)
        metadata->lastUsedPages.cachedPage[i].blkno = InvalidBlockNumber;
}

void SpGistInitPage ( Page  page,
uint16  f 
)

Definition at line 449 of file spgutils.c.

References SpGistPageOpaqueData::flags, MAXALIGN, PageInit(), SpGistPageOpaqueData::spgist_page_id, and SpGistPageGetOpaque.

Referenced by spgbuildempty(), SpGistInitBuffer(), and SpGistInitMetapage().

{
    SpGistPageOpaque opaque;

    PageInit(page, BLCKSZ, MAXALIGN(sizeof(SpGistPageOpaqueData)));
    opaque = SpGistPageGetOpaque(page);
    memset(opaque, 0, sizeof(SpGistPageOpaqueData));
    opaque->flags = f;
    opaque->spgist_page_id = SPGIST_PAGE_ID;
}

Buffer SpGistNewBuffer ( Relation  index  ) 

Definition at line 137 of file spgutils.c.

References BUFFER_LOCK_EXCLUSIVE, BUFFER_LOCK_UNLOCK, BufferGetPage, ConditionalLockBuffer(), ExclusiveLock, GetFreeIndexPage(), InvalidBlockNumber, LockBuffer(), LockRelationForExtension(), P_NEW, PageIsEmpty, PageIsNew, ReadBuffer(), RELATION_IS_LOCAL, ReleaseBuffer(), SpGistBlockIsFixed, SpGistPageIsDeleted, and UnlockRelationForExtension().

Referenced by allocNewBuffer(), and spgbuild().

{
    Buffer      buffer;
    bool        needLock;

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

        if (blkno == InvalidBlockNumber)
            break;              /* nothing known to FSM */

        /*
         * The fixed pages shouldn't ever be listed in FSM, but just in case
         * one is, ignore it.
         */
        if (SpGistBlockIsFixed(blkno))
            continue;

        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 (SpGistPageIsDeleted(page) || PageIsEmpty(page))
                return buffer;  /* OK to use */

            LockBuffer(buffer, BUFFER_LOCK_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, BUFFER_LOCK_EXCLUSIVE);

    if (needLock)
        UnlockRelationForExtension(index, ExclusiveLock);

    return buffer;
}

OffsetNumber SpGistPageAddNewItem ( SpGistState state,
Page  page,
Item  item,
Size  size,
OffsetNumber startOffset,
bool  errorOK 
)

Definition at line 786 of file spgutils.c.

References Assert, elog, ERROR, i, InvalidOffsetNumber, MAXALIGN, SpGistPageOpaqueData::nPlaceholder, PageAddItem(), PageGetExactFreeSpace(), PageGetItem, PageGetItemId, PageGetMaxOffsetNumber, PageIndexTupleDelete(), PANIC, SGDTSIZE, SPGIST_PLACEHOLDER, SpGistPageGetOpaque, and SpGistDeadTupleData::tupstate.

Referenced by addLeafTuple(), doPickSplit(), moveLeafs(), spgAddNodeAction(), and spgSplitNodeAction().

{
    SpGistPageOpaque opaque = SpGistPageGetOpaque(page);
    OffsetNumber i,
                maxoff,
                offnum;

    if (opaque->nPlaceholder > 0 &&
        PageGetExactFreeSpace(page) + SGDTSIZE >= MAXALIGN(size))
    {
        /* Try to replace a placeholder */
        maxoff = PageGetMaxOffsetNumber(page);
        offnum = InvalidOffsetNumber;

        for (;;)
        {
            if (startOffset && *startOffset != InvalidOffsetNumber)
                i = *startOffset;
            else
                i = FirstOffsetNumber;
            for (; i <= maxoff; i++)
            {
                SpGistDeadTuple it = (SpGistDeadTuple) PageGetItem(page,
                                                     PageGetItemId(page, i));

                if (it->tupstate == SPGIST_PLACEHOLDER)
                {
                    offnum = i;
                    break;
                }
            }

            /* Done if we found a placeholder */
            if (offnum != InvalidOffsetNumber)
                break;

            if (startOffset && *startOffset != InvalidOffsetNumber)
            {
                /* Hint was no good, re-search from beginning */
                *startOffset = InvalidOffsetNumber;
                continue;
            }

            /* Hmm, no placeholder found? */
            opaque->nPlaceholder = 0;
            break;
        }

        if (offnum != InvalidOffsetNumber)
        {
            /* Replace the placeholder tuple */
            PageIndexTupleDelete(page, offnum);

            offnum = PageAddItem(page, item, size, offnum, false, false);

            /*
             * We should not have failed given the size check at the top of
             * the function, but test anyway.  If we did fail, we must PANIC
             * because we've already deleted the placeholder tuple, and
             * there's no other way to keep the damage from getting to disk.
             */
            if (offnum != InvalidOffsetNumber)
            {
                Assert(opaque->nPlaceholder > 0);
                opaque->nPlaceholder--;
                if (startOffset)
                    *startOffset = offnum + 1;
            }
            else
                elog(PANIC, "failed to add item of size %u to SPGiST index page",
                     (int) size);

            return offnum;
        }
    }

    /* No luck in replacing a placeholder, so just add it to the page */
    offnum = PageAddItem(page, item, size,
                         InvalidOffsetNumber, false, false);

    if (offnum == InvalidOffsetNumber && !errorOK)
        elog(ERROR, "failed to add item of size %u to SPGiST index page",
             (int) size);

    return offnum;
}

void SpGistSetLastUsedPage ( Relation  index,
Buffer  buffer 
)

Definition at line 414 of file spgutils.c.

References SpGistLastUsedPage::blkno, BufferGetBlockNumber(), BufferGetPage, SpGistLastUsedPage::freeSpace, GBUF_INNER_PARITY, GET_LUP, InvalidBlockNumber, PageGetExactFreeSpace(), spgGetCache(), SpGistBlockIsFixed, SpGistPageIsLeaf, and SpGistPageStoresNulls.

Referenced by doPickSplit(), moveLeafs(), spgAddNodeAction(), spgdoinsert(), spgMatchNodeAction(), spgprocesspending(), spgSplitNodeAction(), and spgvacuumpage().

{
    SpGistCache *cache = spgGetCache(index);
    SpGistLastUsedPage *lup;
    int         freeSpace;
    Page        page = BufferGetPage(buffer);
    BlockNumber blkno = BufferGetBlockNumber(buffer);
    int         flags;

    /* Never enter fixed pages (root pages) in cache, though */
    if (SpGistBlockIsFixed(blkno))
        return;

    if (SpGistPageIsLeaf(page))
        flags = GBUF_LEAF;
    else
        flags = GBUF_INNER_PARITY(blkno);
    if (SpGistPageStoresNulls(page))
        flags |= GBUF_NULLS;

    lup = GET_LUP(cache, flags);

    freeSpace = PageGetExactFreeSpace(page);
    if (lup->blkno == InvalidBlockNumber || lup->blkno == blkno ||
        lup->freeSpace < freeSpace)
    {
        lup->blkno = blkno;
        lup->freeSpace = freeSpace;
    }
}

void SpGistUpdateMetaPage ( Relation  index  ) 
Datum spgoptions ( PG_FUNCTION_ARGS   ) 

Definition at line 493 of file spgutils.c.

References default_reloptions(), PG_GETARG_BOOL, PG_GETARG_DATUM, PG_RETURN_BYTEA_P, PG_RETURN_NULL, and RELOPT_KIND_SPGIST.

{
    Datum       reloptions = PG_GETARG_DATUM(0);
    bool        validate = PG_GETARG_BOOL(1);
    bytea      *result;

    result = default_reloptions(reloptions, validate, RELOPT_KIND_SPGIST);

    if (result)
        PG_RETURN_BYTEA_P(result);
    PG_RETURN_NULL();
}