#include "postgres.h"#include "access/gin_private.h"#include "access/heapam_xlog.h"#include "catalog/index.h"#include "miscadmin.h"#include "storage/bufmgr.h"#include "storage/smgr.h"#include "storage/indexfsm.h"#include "utils/memutils.h"#include "utils/rel.h"
Go to the source code of this file.
| static IndexTuple addItemPointersToLeafTuple | ( | GinState * | ginstate, | |
| IndexTuple | old, | |||
| ItemPointerData * | items, | |||
| uint32 | nitem, | |||
| GinStatsData * | buildStats | |||
| ) | [static] |
Definition at line 105 of file gininsert.c.
References Assert, GinPostingTreeScan::btree, createPostingTree(), FALSE, GinFormTuple(), GinGetNPosting, GinGetPosting, ginInsertItemPointers(), GinIsPostingTree, ginMergeItemPointers(), ginPrepareScanPostingTree(), GinSetPostingTree, GinShortenTuple(), gintuple_get_attrnum(), gintuple_get_key(), GinState::index, GinBtreeData::isBuild, GinStatsData::nDataPages, NULL, and pfree().
Referenced by ginEntryInsert().
{
OffsetNumber attnum;
Datum key;
GinNullCategory category;
IndexTuple res;
Assert(!GinIsPostingTree(old));
attnum = gintuple_get_attrnum(ginstate, old);
key = gintuple_get_key(ginstate, old, &category);
/* try to build tuple with room for all the items */
res = GinFormTuple(ginstate, attnum, key, category,
NULL, nitem + GinGetNPosting(old),
false);
if (res)
{
/* good, small enough */
uint32 newnitem;
/* fill in the posting list with union of old and new TIDs */
newnitem = ginMergeItemPointers(GinGetPosting(res),
GinGetPosting(old),
GinGetNPosting(old),
items, nitem);
/* merge might have eliminated some duplicate items */
GinShortenTuple(res, newnitem);
}
else
{
/* posting list would be too big, convert to posting tree */
BlockNumber postingRoot;
GinPostingTreeScan *gdi;
/*
* Initialize posting tree with the old tuple's posting list. It's
* surely small enough to fit on one posting-tree page, and should
* already be in order with no duplicates.
*/
postingRoot = createPostingTree(ginstate->index,
GinGetPosting(old),
GinGetNPosting(old));
/* During index build, count the newly-added data page */
if (buildStats)
buildStats->nDataPages++;
/* Now insert the TIDs-to-be-added into the posting tree */
gdi = ginPrepareScanPostingTree(ginstate->index, postingRoot, FALSE);
gdi->btree.isBuild = (buildStats != NULL);
ginInsertItemPointers(gdi, items, nitem, buildStats);
pfree(gdi);
/* And build a new posting-tree-only result tuple */
res = GinFormTuple(ginstate, attnum, key, category, NULL, 0, true);
GinSetPostingTree(res, postingRoot);
}
return res;
}
| static IndexTuple buildFreshLeafTuple | ( | GinState * | ginstate, | |
| OffsetNumber | attnum, | |||
| Datum | key, | |||
| GinNullCategory | category, | |||
| ItemPointerData * | items, | |||
| uint32 | nitem, | |||
| GinStatsData * | buildStats | |||
| ) | [static] |
Definition at line 182 of file gininsert.c.
References GinPostingTreeScan::btree, createPostingTree(), FALSE, GinFormTuple(), ginInsertItemPointers(), GinMaxLeafDataItems, ginPrepareScanPostingTree(), GinSetPostingTree, GinState::index, GinBtreeData::isBuild, Min, GinStatsData::nDataPages, NULL, and pfree().
Referenced by ginEntryInsert().
{
IndexTuple res;
/* try to build tuple with room for all the items */
res = GinFormTuple(ginstate, attnum, key, category,
items, nitem, false);
if (!res)
{
/* posting list would be too big, build posting tree */
BlockNumber postingRoot;
/*
* Build posting-tree-only result tuple. We do this first so as to
* fail quickly if the key is too big.
*/
res = GinFormTuple(ginstate, attnum, key, category, NULL, 0, true);
/*
* Initialize posting tree with as many TIDs as will fit on the first
* page.
*/
postingRoot = createPostingTree(ginstate->index,
items,
Min(nitem, GinMaxLeafDataItems));
/* During index build, count the newly-added data page */
if (buildStats)
buildStats->nDataPages++;
/* Add any remaining TIDs to the posting tree */
if (nitem > GinMaxLeafDataItems)
{
GinPostingTreeScan *gdi;
gdi = ginPrepareScanPostingTree(ginstate->index, postingRoot, FALSE);
gdi->btree.isBuild = (buildStats != NULL);
ginInsertItemPointers(gdi,
items + GinMaxLeafDataItems,
nitem - GinMaxLeafDataItems,
buildStats);
pfree(gdi);
}
/* And save the root link in the result tuple */
GinSetPostingTree(res, postingRoot);
}
return res;
}
| static BlockNumber createPostingTree | ( | Relation | index, | |
| ItemPointerData * | items, | |||
| uint32 | nitems | |||
| ) | [static] |
Definition at line 45 of file gininsert.c.
References Assert, ginxlogCreatePostingTree::blkno, XLogRecData::buffer, BufferGetBlockNumber(), BufferGetPage, XLogRecData::data, END_CRIT_SECTION, GIN_DATA, GIN_LEAF, GinDataPageGetData, GinInitBuffer(), GinMaxLeafDataItems, GinNewBuffer(), GinPageGetOpaque, XLogRecData::len, MarkBufferDirty(), XLogRecData::next, ginxlogCreatePostingTree::nitem, ginxlogCreatePostingTree::node, PageSetLSN, RelationData::rd_node, RelationNeedsWAL, START_CRIT_SECTION, UnlockReleaseBuffer(), XLOG_GIN_CREATE_PTREE, and XLogInsert().
Referenced by addItemPointersToLeafTuple(), and buildFreshLeafTuple().
{
BlockNumber blkno;
Buffer buffer = GinNewBuffer(index);
Page page;
/* Assert that the items[] array will fit on one page */
Assert(nitems <= GinMaxLeafDataItems);
START_CRIT_SECTION();
GinInitBuffer(buffer, GIN_DATA | GIN_LEAF);
page = BufferGetPage(buffer);
blkno = BufferGetBlockNumber(buffer);
memcpy(GinDataPageGetData(page), items, sizeof(ItemPointerData) * nitems);
GinPageGetOpaque(page)->maxoff = nitems;
MarkBufferDirty(buffer);
if (RelationNeedsWAL(index))
{
XLogRecPtr recptr;
XLogRecData rdata[2];
ginxlogCreatePostingTree data;
data.node = index->rd_node;
data.blkno = blkno;
data.nitem = nitems;
rdata[0].buffer = InvalidBuffer;
rdata[0].data = (char *) &data;
rdata[0].len = sizeof(ginxlogCreatePostingTree);
rdata[0].next = &rdata[1];
rdata[1].buffer = InvalidBuffer;
rdata[1].data = (char *) items;
rdata[1].len = sizeof(ItemPointerData) * nitems;
rdata[1].next = NULL;
recptr = XLogInsert(RM_GIN_ID, XLOG_GIN_CREATE_PTREE, rdata);
PageSetLSN(page, recptr);
}
UnlockReleaseBuffer(buffer);
END_CRIT_SECTION();
return blkno;
}
| Datum ginbuild | ( | PG_FUNCTION_ARGS | ) |
Definition at line 381 of file gininsert.c.
References GinBuildState::accum, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE, ALLOCSET_DEFAULT_MINSIZE, AllocSetContextCreate(), XLogRecData::buffer, BufferGetPage, GinBuildState::buildStats, CHECK_FOR_INTERRUPTS, CurrentMemoryContext, XLogRecData::data, elog, END_CRIT_SECTION, ERROR, GinBuildState::funcCtx, GIN_LEAF, ginBeginBAScan(), ginBuildCallback(), ginEntryInsert(), ginGetBAEntry(), ginInitBA(), GinInitBuffer(), GinInitMetabuffer(), GinNewBuffer(), BuildAccumulator::ginstate, GinBuildState::ginstate, ginUpdateStats(), IndexBuildResult::heap_tuples, IndexBuildResult::index_tuples, IndexBuildHeapScan(), GinBuildState::indtuples, initGinState(), XLogRecData::len, sort-test::list, MarkBufferDirty(), MemoryContextDelete(), MemoryContextSwitchTo(), GinStatsData::nEntryPages, XLogRecData::next, NULL, PageSetLSN, palloc(), PG_GETARG_POINTER, PG_RETURN_POINTER, RelationData::rd_node, RelationGetNumberOfBlocks, RelationGetRelationName, RelationNeedsWAL, START_CRIT_SECTION, GinBuildState::tmpCtx, UnlockReleaseBuffer(), XLOG_GIN_CREATE_INDEX, and XLogInsert().
{
Relation heap = (Relation) PG_GETARG_POINTER(0);
Relation index = (Relation) PG_GETARG_POINTER(1);
IndexInfo *indexInfo = (IndexInfo *) PG_GETARG_POINTER(2);
IndexBuildResult *result;
double reltuples;
GinBuildState buildstate;
Buffer RootBuffer,
MetaBuffer;
ItemPointerData *list;
Datum key;
GinNullCategory category;
uint32 nlist;
MemoryContext oldCtx;
OffsetNumber attnum;
if (RelationGetNumberOfBlocks(index) != 0)
elog(ERROR, "index \"%s\" already contains data",
RelationGetRelationName(index));
initGinState(&buildstate.ginstate, index);
buildstate.indtuples = 0;
memset(&buildstate.buildStats, 0, sizeof(GinStatsData));
/* initialize the meta page */
MetaBuffer = GinNewBuffer(index);
/* initialize the root page */
RootBuffer = GinNewBuffer(index);
START_CRIT_SECTION();
GinInitMetabuffer(MetaBuffer);
MarkBufferDirty(MetaBuffer);
GinInitBuffer(RootBuffer, GIN_LEAF);
MarkBufferDirty(RootBuffer);
if (RelationNeedsWAL(index))
{
XLogRecPtr recptr;
XLogRecData rdata;
Page page;
rdata.buffer = InvalidBuffer;
rdata.data = (char *) &(index->rd_node);
rdata.len = sizeof(RelFileNode);
rdata.next = NULL;
recptr = XLogInsert(RM_GIN_ID, XLOG_GIN_CREATE_INDEX, &rdata);
page = BufferGetPage(RootBuffer);
PageSetLSN(page, recptr);
page = BufferGetPage(MetaBuffer);
PageSetLSN(page, recptr);
}
UnlockReleaseBuffer(MetaBuffer);
UnlockReleaseBuffer(RootBuffer);
END_CRIT_SECTION();
/* count the root as first entry page */
buildstate.buildStats.nEntryPages++;
/*
* create a temporary memory context that is reset once for each tuple
* inserted into the index
*/
buildstate.tmpCtx = AllocSetContextCreate(CurrentMemoryContext,
"Gin build temporary context",
ALLOCSET_DEFAULT_MINSIZE,
ALLOCSET_DEFAULT_INITSIZE,
ALLOCSET_DEFAULT_MAXSIZE);
buildstate.funcCtx = AllocSetContextCreate(buildstate.tmpCtx,
"Gin build temporary context for user-defined function",
ALLOCSET_DEFAULT_MINSIZE,
ALLOCSET_DEFAULT_INITSIZE,
ALLOCSET_DEFAULT_MAXSIZE);
buildstate.accum.ginstate = &buildstate.ginstate;
ginInitBA(&buildstate.accum);
/*
* Do the heap scan. We disallow sync scan here because dataPlaceToPage
* prefers to receive tuples in TID order.
*/
reltuples = IndexBuildHeapScan(heap, index, indexInfo, false,
ginBuildCallback, (void *) &buildstate);
/* dump remaining entries to the index */
oldCtx = MemoryContextSwitchTo(buildstate.tmpCtx);
ginBeginBAScan(&buildstate.accum);
while ((list = ginGetBAEntry(&buildstate.accum,
&attnum, &key, &category, &nlist)) != NULL)
{
/* there could be many entries, so be willing to abort here */
CHECK_FOR_INTERRUPTS();
ginEntryInsert(&buildstate.ginstate, attnum, key, category,
list, nlist, &buildstate.buildStats);
}
MemoryContextSwitchTo(oldCtx);
MemoryContextDelete(buildstate.tmpCtx);
/*
* Update metapage stats
*/
buildstate.buildStats.nTotalPages = RelationGetNumberOfBlocks(index);
ginUpdateStats(index, &buildstate.buildStats);
/*
* Return statistics
*/
result = (IndexBuildResult *) palloc(sizeof(IndexBuildResult));
result->heap_tuples = reltuples;
result->index_tuples = buildstate.indtuples;
PG_RETURN_POINTER(result);
}
| static void ginBuildCallback | ( | Relation | index, | |
| HeapTuple | htup, | |||
| Datum * | values, | |||
| bool * | isnull, | |||
| bool | tupleIsAlive, | |||
| void * | state | |||
| ) | [static] |
Definition at line 340 of file gininsert.c.
References GinBuildState::accum, BuildAccumulator::allocatedMemory, GinBuildState::buildStats, CHECK_FOR_INTERRUPTS, ginBeginBAScan(), ginEntryInsert(), ginGetBAEntry(), ginHeapTupleBulkInsert(), ginInitBA(), GinBuildState::ginstate, i, sort-test::list, maintenance_work_mem, MemoryContextReset(), MemoryContextSwitchTo(), tupleDesc::natts, NULL, GinState::origTupdesc, HeapTupleData::t_self, and GinBuildState::tmpCtx.
Referenced by ginbuild().
{
GinBuildState *buildstate = (GinBuildState *) state;
MemoryContext oldCtx;
int i;
oldCtx = MemoryContextSwitchTo(buildstate->tmpCtx);
for (i = 0; i < buildstate->ginstate.origTupdesc->natts; i++)
ginHeapTupleBulkInsert(buildstate, (OffsetNumber) (i + 1),
values[i], isnull[i],
&htup->t_self);
/* If we've maxed out our available memory, dump everything to the index */
if (buildstate->accum.allocatedMemory >= maintenance_work_mem * 1024L)
{
ItemPointerData *list;
Datum key;
GinNullCategory category;
uint32 nlist;
OffsetNumber attnum;
ginBeginBAScan(&buildstate->accum);
while ((list = ginGetBAEntry(&buildstate->accum,
&attnum, &key, &category, &nlist)) != NULL)
{
/* there could be many entries, so be willing to abort here */
CHECK_FOR_INTERRUPTS();
ginEntryInsert(&buildstate->ginstate, attnum, key, category,
list, nlist, &buildstate->buildStats);
}
MemoryContextReset(buildstate->tmpCtx);
ginInitBA(&buildstate->accum);
}
MemoryContextSwitchTo(oldCtx);
}
| Datum ginbuildempty | ( | PG_FUNCTION_ARGS | ) |
Definition at line 507 of file gininsert.c.
References BUFFER_LOCK_EXCLUSIVE, END_CRIT_SECTION, GIN_LEAF, GinInitBuffer(), GinInitMetabuffer(), INIT_FORKNUM, LockBuffer(), log_newpage_buffer(), MarkBufferDirty(), NULL, P_NEW, PG_GETARG_POINTER, PG_RETURN_VOID, RBM_NORMAL, ReadBufferExtended(), START_CRIT_SECTION, and UnlockReleaseBuffer().
{
Relation index = (Relation) PG_GETARG_POINTER(0);
Buffer RootBuffer,
MetaBuffer;
/* An empty GIN index has two pages. */
MetaBuffer =
ReadBufferExtended(index, INIT_FORKNUM, P_NEW, RBM_NORMAL, NULL);
LockBuffer(MetaBuffer, BUFFER_LOCK_EXCLUSIVE);
RootBuffer =
ReadBufferExtended(index, INIT_FORKNUM, P_NEW, RBM_NORMAL, NULL);
LockBuffer(RootBuffer, BUFFER_LOCK_EXCLUSIVE);
/* Initialize and xlog metabuffer and root buffer. */
START_CRIT_SECTION();
GinInitMetabuffer(MetaBuffer);
MarkBufferDirty(MetaBuffer);
log_newpage_buffer(MetaBuffer);
GinInitBuffer(RootBuffer, GIN_LEAF);
MarkBufferDirty(RootBuffer);
log_newpage_buffer(RootBuffer);
END_CRIT_SECTION();
/* Unlock and release the buffers. */
UnlockReleaseBuffer(MetaBuffer);
UnlockReleaseBuffer(RootBuffer);
PG_RETURN_VOID();
}
| void ginEntryInsert | ( | GinState * | ginstate, | |
| OffsetNumber | attnum, | |||
| Datum | key, | |||
| GinNullCategory | category, | |||
| ItemPointerData * | items, | |||
| uint32 | nitem, | |||
| GinStatsData * | buildStats | |||
| ) |
Definition at line 247 of file gininsert.c.
References addItemPointersToLeafTuple(), GinPostingTreeScan::btree, GinBtreeStack::buffer, BufferGetPage, buildFreshLeafTuple(), GinBtreeData::entry, FALSE, GinBtreeData::findItem, freeGinBtreeStack(), GIN_UNLOCK, ginFindLeafPage(), GinGetPostingTree, ginInsertItemPointers(), ginInsertValue(), GinIsPostingTree, ginPrepareEntryScan(), ginPrepareScanPostingTree(), GinState::index, GinBtreeData::isBuild, GinBtreeData::isDelete, LockBuffer(), GinStatsData::nEntries, NULL, GinBtreeStack::off, PageGetItem, PageGetItemId, and pfree().
Referenced by ginbuild(), ginBuildCallback(), ginHeapTupleInsert(), and ginInsertCleanup().
{
GinBtreeData btree;
GinBtreeStack *stack;
IndexTuple itup;
Page page;
/* During index build, count the to-be-inserted entry */
if (buildStats)
buildStats->nEntries++;
ginPrepareEntryScan(&btree, attnum, key, category, ginstate);
stack = ginFindLeafPage(&btree, NULL);
page = BufferGetPage(stack->buffer);
if (btree.findItem(&btree, stack))
{
/* found pre-existing entry */
itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, stack->off));
if (GinIsPostingTree(itup))
{
/* add entries to existing posting tree */
BlockNumber rootPostingTree = GinGetPostingTree(itup);
GinPostingTreeScan *gdi;
/* release all stack */
LockBuffer(stack->buffer, GIN_UNLOCK);
freeGinBtreeStack(stack);
/* insert into posting tree */
gdi = ginPrepareScanPostingTree(ginstate->index, rootPostingTree, FALSE);
gdi->btree.isBuild = (buildStats != NULL);
ginInsertItemPointers(gdi, items, nitem, buildStats);
pfree(gdi);
return;
}
/* modify an existing leaf entry */
itup = addItemPointersToLeafTuple(ginstate, itup,
items, nitem, buildStats);
btree.isDelete = TRUE;
}
else
{
/* no match, so construct a new leaf entry */
itup = buildFreshLeafTuple(ginstate, attnum, key, category,
items, nitem, buildStats);
}
/* Insert the new or modified leaf tuple */
btree.entry = itup;
ginInsertValue(&btree, stack, buildStats);
pfree(itup);
}
| static void ginHeapTupleBulkInsert | ( | GinBuildState * | buildstate, | |
| OffsetNumber | attnum, | |||
| Datum | value, | |||
| bool | isNull, | |||
| ItemPointer | heapptr | |||
| ) | [static] |
Definition at line 316 of file gininsert.c.
References GinBuildState::accum, GinBuildState::funcCtx, ginExtractEntries(), ginInsertBAEntries(), BuildAccumulator::ginstate, GinBuildState::indtuples, MemoryContextReset(), and MemoryContextSwitchTo().
Referenced by ginBuildCallback().
{
Datum *entries;
GinNullCategory *categories;
int32 nentries;
MemoryContext oldCtx;
oldCtx = MemoryContextSwitchTo(buildstate->funcCtx);
entries = ginExtractEntries(buildstate->accum.ginstate, attnum,
value, isNull,
&nentries, &categories);
MemoryContextSwitchTo(oldCtx);
ginInsertBAEntries(&buildstate->accum, heapptr, attnum,
entries, categories, nentries);
buildstate->indtuples += nentries;
MemoryContextReset(buildstate->funcCtx);
}
| static void ginHeapTupleInsert | ( | GinState * | ginstate, | |
| OffsetNumber | attnum, | |||
| Datum | value, | |||
| bool | isNull, | |||
| ItemPointer | item | |||
| ) | [static] |
Definition at line 543 of file gininsert.c.
References ginEntryInsert(), ginExtractEntries(), i, and NULL.
Referenced by gininsert().
{
Datum *entries;
GinNullCategory *categories;
int32 i,
nentries;
entries = ginExtractEntries(ginstate, attnum, value, isNull,
&nentries, &categories);
for (i = 0; i < nentries; i++)
ginEntryInsert(ginstate, attnum, entries[i], categories[i],
item, 1, NULL);
}
| Datum gininsert | ( | PG_FUNCTION_ARGS | ) |
Definition at line 561 of file gininsert.c.
References ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE, ALLOCSET_DEFAULT_MINSIZE, AllocSetContextCreate(), CurrentMemoryContext, GinGetUseFastUpdate, ginHeapTupleFastCollect(), ginHeapTupleFastInsert(), ginHeapTupleInsert(), i, initGinState(), MemoryContextDelete(), MemoryContextSwitchTo(), tupleDesc::natts, GinState::origTupdesc, PG_GETARG_INT32, PG_GETARG_POINTER, PG_RETURN_BOOL, and values.
{
Relation index = (Relation) PG_GETARG_POINTER(0);
Datum *values = (Datum *) PG_GETARG_POINTER(1);
bool *isnull = (bool *) PG_GETARG_POINTER(2);
ItemPointer ht_ctid = (ItemPointer) PG_GETARG_POINTER(3);
#ifdef NOT_USED
Relation heapRel = (Relation) PG_GETARG_POINTER(4);
IndexUniqueCheck checkUnique = (IndexUniqueCheck) PG_GETARG_INT32(5);
#endif
GinState ginstate;
MemoryContext oldCtx;
MemoryContext insertCtx;
int i;
insertCtx = AllocSetContextCreate(CurrentMemoryContext,
"Gin insert temporary context",
ALLOCSET_DEFAULT_MINSIZE,
ALLOCSET_DEFAULT_INITSIZE,
ALLOCSET_DEFAULT_MAXSIZE);
oldCtx = MemoryContextSwitchTo(insertCtx);
initGinState(&ginstate, index);
if (GinGetUseFastUpdate(index))
{
GinTupleCollector collector;
memset(&collector, 0, sizeof(GinTupleCollector));
for (i = 0; i < ginstate.origTupdesc->natts; i++)
ginHeapTupleFastCollect(&ginstate, &collector,
(OffsetNumber) (i + 1),
values[i], isnull[i],
ht_ctid);
ginHeapTupleFastInsert(&ginstate, &collector);
}
else
{
for (i = 0; i < ginstate.origTupdesc->natts; i++)
ginHeapTupleInsert(&ginstate, (OffsetNumber) (i + 1),
values[i], isnull[i],
ht_ctid);
}
MemoryContextSwitchTo(oldCtx);
MemoryContextDelete(insertCtx);
PG_RETURN_BOOL(false);
}
1.7.1