#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"
Go to the source code of this file.
| #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().
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 | |||
| ) |
Definition at line 713 of file spgutils.c.
References Assert, SpGistState::deadTupleStorage, InvalidOffsetNumber, ItemPointerSet, ItemPointerSetInvalid, SpGistState::myXid, SpGistDeadTupleData::nextOffset, SpGistDeadTupleData::pointer, SGDTSIZE, SpGistDeadTupleData::size, SPGIST_REDIRECT, TransactionIdIsValid, SpGistDeadTupleData::tupstate, and SpGistDeadTupleData::xid.
Referenced by spgAddNodeAction(), spgPageIndexMultiDelete(), and spgRedoAddNode().
{
SpGistDeadTuple tuple = (SpGistDeadTuple) state->deadTupleStorage;
tuple->tupstate = tupstate;
tuple->size = SGDTSIZE;
tuple->nextOffset = InvalidOffsetNumber;
if (tupstate == SPGIST_REDIRECT)
{
ItemPointerSet(&tuple->pointer, blkno, offnum);
Assert(TransactionIdIsValid(state->myXid));
tuple->xid = state->myXid;
}
else
{
ItemPointerSetInvalid(&tuple->pointer);
tuple->xid = InvalidTransactionId;
}
return tuple;
}
| 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;
}
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);
}
Definition at line 464 of file spgutils.c.
References Assert, BufferGetPage, BufferGetPageSize, and SpGistInitPage().
Referenced by allocNewBuffer(), doPickSplit(), spgbuild(), SpGistGetBuffer(), spgRedoAddLeaf(), spgRedoAddNode(), spgRedoCreateIndex(), spgRedoMoveLeafs(), spgRedoPickSplit(), spgRedoSplitTuple(), and spgvacuumpage().
{
Assert(BufferGetPageSize(b) == BLCKSZ);
SpGistInitPage(BufferGetPage(b), 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;
}
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;
}
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;
}
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 | ) |
Definition at line 202 of file spgutils.c.
References BufferGetPage, ConditionalLockBuffer(), SpGistCache::lastUsedPages, SpGistMetaPageData::lastUsedPages, MarkBufferDirty(), NULL, RelationData::rd_amcache, ReadBuffer(), ReleaseBuffer(), SPGIST_METAPAGE_BLKNO, SpGistPageGetMeta, and UnlockReleaseBuffer().
Referenced by spgbuild(), spginsert(), and spgvacuumscan().
{
SpGistCache *cache = (SpGistCache *) index->rd_amcache;
if (cache != NULL)
{
Buffer metabuffer;
SpGistMetaPageData *metadata;
metabuffer = ReadBuffer(index, SPGIST_METAPAGE_BLKNO);
if (ConditionalLockBuffer(metabuffer))
{
metadata = SpGistPageGetMeta(BufferGetPage(metabuffer));
metadata->lastUsedPages = cache->lastUsedPages;
MarkBufferDirty(metabuffer);
UnlockReleaseBuffer(metabuffer);
}
else
{
ReleaseBuffer(metabuffer);
}
}
}
| 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();
}
1.7.1