2006-05-02 11:28:56 +00:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* ginxlog.c
|
|
|
|
* WAL replay logic for inverted index.
|
|
|
|
*
|
|
|
|
*
|
2010-01-02 16:58:17 +00:00
|
|
|
* Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
|
2006-05-02 11:28:56 +00:00
|
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
|
|
|
*
|
|
|
|
* IDENTIFICATION
|
2010-09-20 22:08:53 +02:00
|
|
|
* src/backend/access/gin/ginxlog.c
|
2006-05-02 11:28:56 +00:00
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
#include "postgres.h"
|
|
|
|
|
|
|
|
#include "access/gin.h"
|
2008-05-12 00:00:54 +00:00
|
|
|
#include "access/xlogutils.h"
|
|
|
|
#include "storage/bufmgr.h"
|
2006-05-02 11:28:56 +00:00
|
|
|
#include "utils/memutils.h"
|
|
|
|
|
2006-10-04 00:30:14 +00:00
|
|
|
static MemoryContext opCtx; /* working memory for operations */
|
2006-05-02 11:28:56 +00:00
|
|
|
static MemoryContext topCtx;
|
|
|
|
|
2006-10-04 00:30:14 +00:00
|
|
|
typedef struct ginIncompleteSplit
|
|
|
|
{
|
|
|
|
RelFileNode node;
|
|
|
|
BlockNumber leftBlkno;
|
2006-05-02 11:28:56 +00:00
|
|
|
BlockNumber rightBlkno;
|
|
|
|
BlockNumber rootBlkno;
|
|
|
|
} ginIncompleteSplit;
|
|
|
|
|
|
|
|
static List *incomplete_splits;
|
|
|
|
|
|
|
|
static void
|
2006-10-04 00:30:14 +00:00
|
|
|
pushIncompleteSplit(RelFileNode node, BlockNumber leftBlkno, BlockNumber rightBlkno, BlockNumber rootBlkno)
|
|
|
|
{
|
|
|
|
ginIncompleteSplit *split;
|
2006-05-02 11:28:56 +00:00
|
|
|
|
2006-10-04 00:30:14 +00:00
|
|
|
MemoryContextSwitchTo(topCtx);
|
2006-05-02 11:28:56 +00:00
|
|
|
|
|
|
|
split = palloc(sizeof(ginIncompleteSplit));
|
|
|
|
|
|
|
|
split->node = node;
|
|
|
|
split->leftBlkno = leftBlkno;
|
|
|
|
split->rightBlkno = rightBlkno;
|
|
|
|
split->rootBlkno = rootBlkno;
|
|
|
|
|
|
|
|
incomplete_splits = lappend(incomplete_splits, split);
|
|
|
|
|
2006-10-04 00:30:14 +00:00
|
|
|
MemoryContextSwitchTo(opCtx);
|
2006-05-02 11:28:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2006-10-04 00:30:14 +00:00
|
|
|
forgetIncompleteSplit(RelFileNode node, BlockNumber leftBlkno, BlockNumber updateBlkno)
|
|
|
|
{
|
2006-05-02 11:28:56 +00:00
|
|
|
ListCell *l;
|
|
|
|
|
2006-10-04 00:30:14 +00:00
|
|
|
foreach(l, incomplete_splits)
|
|
|
|
{
|
|
|
|
ginIncompleteSplit *split = (ginIncompleteSplit *) lfirst(l);
|
2006-05-02 11:28:56 +00:00
|
|
|
|
2006-10-04 00:30:14 +00:00
|
|
|
if (RelFileNodeEquals(node, split->node) && leftBlkno == split->leftBlkno && updateBlkno == split->rightBlkno)
|
|
|
|
{
|
2006-05-02 11:28:56 +00:00
|
|
|
incomplete_splits = list_delete_ptr(incomplete_splits, split);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2006-10-04 00:30:14 +00:00
|
|
|
ginRedoCreateIndex(XLogRecPtr lsn, XLogRecord *record)
|
|
|
|
{
|
2006-05-02 11:28:56 +00:00
|
|
|
RelFileNode *node = (RelFileNode *) XLogRecGetData(record);
|
2009-06-11 14:49:15 +00:00
|
|
|
Buffer RootBuffer,
|
|
|
|
MetaBuffer;
|
2006-05-02 11:28:56 +00:00
|
|
|
Page page;
|
|
|
|
|
2009-03-24 20:17:18 +00:00
|
|
|
MetaBuffer = XLogReadBuffer(*node, GIN_METAPAGE_BLKNO, true);
|
|
|
|
Assert(BufferIsValid(MetaBuffer));
|
|
|
|
GinInitMetabuffer(MetaBuffer);
|
|
|
|
|
|
|
|
page = (Page) BufferGetPage(MetaBuffer);
|
|
|
|
PageSetLSN(page, lsn);
|
|
|
|
PageSetTLI(page, ThisTimeLineID);
|
2006-05-02 11:28:56 +00:00
|
|
|
|
2009-03-24 20:17:18 +00:00
|
|
|
RootBuffer = XLogReadBuffer(*node, GIN_ROOT_BLKNO, true);
|
|
|
|
Assert(BufferIsValid(RootBuffer));
|
|
|
|
page = (Page) BufferGetPage(RootBuffer);
|
|
|
|
|
|
|
|
GinInitBuffer(RootBuffer, GIN_LEAF);
|
2006-05-02 11:28:56 +00:00
|
|
|
|
|
|
|
PageSetLSN(page, lsn);
|
|
|
|
PageSetTLI(page, ThisTimeLineID);
|
|
|
|
|
2009-03-24 20:17:18 +00:00
|
|
|
MarkBufferDirty(MetaBuffer);
|
|
|
|
UnlockReleaseBuffer(MetaBuffer);
|
|
|
|
MarkBufferDirty(RootBuffer);
|
|
|
|
UnlockReleaseBuffer(RootBuffer);
|
2006-05-02 11:28:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2006-10-04 00:30:14 +00:00
|
|
|
ginRedoCreatePTree(XLogRecPtr lsn, XLogRecord *record)
|
|
|
|
{
|
|
|
|
ginxlogCreatePostingTree *data = (ginxlogCreatePostingTree *) XLogRecGetData(record);
|
|
|
|
ItemPointerData *items = (ItemPointerData *) (XLogRecGetData(record) + sizeof(ginxlogCreatePostingTree));
|
2006-05-02 11:28:56 +00:00
|
|
|
Buffer buffer;
|
|
|
|
Page page;
|
|
|
|
|
2008-06-12 09:12:31 +00:00
|
|
|
buffer = XLogReadBuffer(data->node, data->blkno, true);
|
2006-05-02 11:28:56 +00:00
|
|
|
Assert(BufferIsValid(buffer));
|
|
|
|
page = (Page) BufferGetPage(buffer);
|
|
|
|
|
2006-10-04 00:30:14 +00:00
|
|
|
GinInitBuffer(buffer, GIN_DATA | GIN_LEAF);
|
|
|
|
memcpy(GinDataPageGetData(page), items, sizeof(ItemPointerData) * data->nitem);
|
2006-05-02 11:28:56 +00:00
|
|
|
GinPageGetOpaque(page)->maxoff = data->nitem;
|
|
|
|
|
|
|
|
PageSetLSN(page, lsn);
|
|
|
|
PageSetTLI(page, ThisTimeLineID);
|
|
|
|
|
|
|
|
MarkBufferDirty(buffer);
|
|
|
|
UnlockReleaseBuffer(buffer);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2006-10-04 00:30:14 +00:00
|
|
|
ginRedoInsert(XLogRecPtr lsn, XLogRecord *record)
|
|
|
|
{
|
|
|
|
ginxlogInsert *data = (ginxlogInsert *) XLogRecGetData(record);
|
2006-05-02 11:28:56 +00:00
|
|
|
Buffer buffer;
|
|
|
|
Page page;
|
|
|
|
|
2007-10-29 19:26:57 +00:00
|
|
|
/* nothing else to do if page was backed up */
|
2006-05-02 11:28:56 +00:00
|
|
|
if (record->xl_info & XLR_BKP_BLOCK_1)
|
|
|
|
return;
|
|
|
|
|
2008-06-12 09:12:31 +00:00
|
|
|
buffer = XLogReadBuffer(data->node, data->blkno, false);
|
2006-05-02 11:28:56 +00:00
|
|
|
Assert(BufferIsValid(buffer));
|
|
|
|
page = (Page) BufferGetPage(buffer);
|
|
|
|
|
2006-10-04 00:30:14 +00:00
|
|
|
if (data->isData)
|
|
|
|
{
|
|
|
|
Assert(data->isDelete == FALSE);
|
|
|
|
Assert(GinPageIsData(page));
|
2006-05-02 11:28:56 +00:00
|
|
|
|
2007-11-15 21:14:46 +00:00
|
|
|
if (!XLByteLE(lsn, PageGetLSN(page)))
|
2006-10-04 00:30:14 +00:00
|
|
|
{
|
2007-10-29 19:26:57 +00:00
|
|
|
if (data->isLeaf)
|
|
|
|
{
|
|
|
|
OffsetNumber i;
|
|
|
|
ItemPointerData *items = (ItemPointerData *) (XLogRecGetData(record) + sizeof(ginxlogInsert));
|
2006-05-02 11:28:56 +00:00
|
|
|
|
2007-10-29 19:26:57 +00:00
|
|
|
Assert(GinPageIsLeaf(page));
|
|
|
|
Assert(data->updateBlkno == InvalidBlockNumber);
|
2006-05-02 11:28:56 +00:00
|
|
|
|
2007-10-29 19:26:57 +00:00
|
|
|
for (i = 0; i < data->nitem; i++)
|
|
|
|
GinDataPageAddItem(page, items + i, data->offset + i);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
PostingItem *pitem;
|
2006-05-02 11:28:56 +00:00
|
|
|
|
2007-10-29 19:26:57 +00:00
|
|
|
Assert(!GinPageIsLeaf(page));
|
2006-05-02 11:28:56 +00:00
|
|
|
|
2007-10-29 19:26:57 +00:00
|
|
|
if (data->updateBlkno != InvalidBlockNumber)
|
|
|
|
{
|
|
|
|
/* update link to right page after split */
|
|
|
|
pitem = (PostingItem *) GinDataPageGetItem(page, data->offset);
|
|
|
|
PostingItemSetBlockNumber(pitem, data->updateBlkno);
|
|
|
|
}
|
2006-05-02 11:28:56 +00:00
|
|
|
|
2007-10-29 19:26:57 +00:00
|
|
|
pitem = (PostingItem *) (XLogRecGetData(record) + sizeof(ginxlogInsert));
|
2006-05-02 11:28:56 +00:00
|
|
|
|
2007-10-29 19:26:57 +00:00
|
|
|
GinDataPageAddItem(page, pitem, data->offset);
|
|
|
|
}
|
|
|
|
}
|
2006-05-02 11:28:56 +00:00
|
|
|
|
2007-10-29 19:26:57 +00:00
|
|
|
if (!data->isLeaf && data->updateBlkno != InvalidBlockNumber)
|
|
|
|
{
|
|
|
|
PostingItem *pitem = (PostingItem *) (XLogRecGetData(record) + sizeof(ginxlogInsert));
|
2007-11-15 21:14:46 +00:00
|
|
|
|
2007-10-29 19:26:57 +00:00
|
|
|
forgetIncompleteSplit(data->node, PostingItemGetBlockNumber(pitem), data->updateBlkno);
|
2006-05-02 11:28:56 +00:00
|
|
|
}
|
2007-10-29 19:26:57 +00:00
|
|
|
|
2006-10-04 00:30:14 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
IndexTuple itup;
|
2006-05-02 11:28:56 +00:00
|
|
|
|
2006-10-04 00:30:14 +00:00
|
|
|
Assert(!GinPageIsData(page));
|
2006-05-02 11:28:56 +00:00
|
|
|
|
2007-11-15 21:14:46 +00:00
|
|
|
if (!XLByteLE(lsn, PageGetLSN(page)))
|
2006-10-04 00:30:14 +00:00
|
|
|
{
|
2007-10-29 19:26:57 +00:00
|
|
|
if (data->updateBlkno != InvalidBlockNumber)
|
|
|
|
{
|
|
|
|
/* update link to right page after split */
|
|
|
|
Assert(!GinPageIsLeaf(page));
|
|
|
|
Assert(data->offset >= FirstOffsetNumber && data->offset <= PageGetMaxOffsetNumber(page));
|
|
|
|
itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, data->offset));
|
|
|
|
ItemPointerSet(&itup->t_tid, data->updateBlkno, InvalidOffsetNumber);
|
|
|
|
}
|
2006-05-02 11:28:56 +00:00
|
|
|
|
2007-10-29 19:26:57 +00:00
|
|
|
if (data->isDelete)
|
|
|
|
{
|
|
|
|
Assert(GinPageIsLeaf(page));
|
|
|
|
Assert(data->offset >= FirstOffsetNumber && data->offset <= PageGetMaxOffsetNumber(page));
|
|
|
|
PageIndexTupleDelete(page, data->offset);
|
|
|
|
}
|
2006-05-02 11:28:56 +00:00
|
|
|
|
2007-10-29 19:26:57 +00:00
|
|
|
itup = (IndexTuple) (XLogRecGetData(record) + sizeof(ginxlogInsert));
|
2006-05-02 11:28:56 +00:00
|
|
|
|
2007-10-29 19:26:57 +00:00
|
|
|
if (PageAddItem(page, (Item) itup, IndexTupleSize(itup), data->offset, false, false) == InvalidOffsetNumber)
|
|
|
|
elog(ERROR, "failed to add item to index page in %u/%u/%u",
|
2007-11-15 21:14:46 +00:00
|
|
|
data->node.spcNode, data->node.dbNode, data->node.relNode);
|
2007-10-29 19:26:57 +00:00
|
|
|
}
|
2006-05-02 11:28:56 +00:00
|
|
|
|
2006-10-04 00:30:14 +00:00
|
|
|
if (!data->isLeaf && data->updateBlkno != InvalidBlockNumber)
|
2007-10-29 19:26:57 +00:00
|
|
|
{
|
|
|
|
itup = (IndexTuple) (XLogRecGetData(record) + sizeof(ginxlogInsert));
|
2006-10-04 00:30:14 +00:00
|
|
|
forgetIncompleteSplit(data->node, GinItemPointerGetBlockNumber(&itup->t_tid), data->updateBlkno);
|
2007-10-29 19:26:57 +00:00
|
|
|
}
|
2006-05-02 11:28:56 +00:00
|
|
|
}
|
|
|
|
|
2007-11-15 21:14:46 +00:00
|
|
|
if (!XLByteLE(lsn, PageGetLSN(page)))
|
2007-10-29 19:26:57 +00:00
|
|
|
{
|
|
|
|
PageSetLSN(page, lsn);
|
|
|
|
PageSetTLI(page, ThisTimeLineID);
|
2006-05-02 11:28:56 +00:00
|
|
|
|
2007-10-29 19:26:57 +00:00
|
|
|
MarkBufferDirty(buffer);
|
|
|
|
}
|
2006-05-02 11:28:56 +00:00
|
|
|
UnlockReleaseBuffer(buffer);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2006-10-04 00:30:14 +00:00
|
|
|
ginRedoSplit(XLogRecPtr lsn, XLogRecord *record)
|
|
|
|
{
|
|
|
|
ginxlogSplit *data = (ginxlogSplit *) XLogRecGetData(record);
|
|
|
|
Buffer lbuffer,
|
|
|
|
rbuffer;
|
|
|
|
Page lpage,
|
|
|
|
rpage;
|
2006-05-02 11:28:56 +00:00
|
|
|
uint32 flags = 0;
|
|
|
|
|
2006-10-04 00:30:14 +00:00
|
|
|
if (data->isLeaf)
|
2006-05-02 11:28:56 +00:00
|
|
|
flags |= GIN_LEAF;
|
2006-10-04 00:30:14 +00:00
|
|
|
if (data->isData)
|
2006-05-02 11:28:56 +00:00
|
|
|
flags |= GIN_DATA;
|
|
|
|
|
2008-06-12 09:12:31 +00:00
|
|
|
lbuffer = XLogReadBuffer(data->node, data->lblkno, data->isRootSplit);
|
2006-05-02 11:28:56 +00:00
|
|
|
Assert(BufferIsValid(lbuffer));
|
|
|
|
lpage = (Page) BufferGetPage(lbuffer);
|
|
|
|
GinInitBuffer(lbuffer, flags);
|
|
|
|
|
2008-06-12 09:12:31 +00:00
|
|
|
rbuffer = XLogReadBuffer(data->node, data->rblkno, true);
|
2006-05-02 11:28:56 +00:00
|
|
|
Assert(BufferIsValid(rbuffer));
|
|
|
|
rpage = (Page) BufferGetPage(rbuffer);
|
|
|
|
GinInitBuffer(rbuffer, flags);
|
|
|
|
|
2006-10-04 00:30:14 +00:00
|
|
|
GinPageGetOpaque(lpage)->rightlink = BufferGetBlockNumber(rbuffer);
|
2006-05-02 11:28:56 +00:00
|
|
|
GinPageGetOpaque(rpage)->rightlink = data->rrlink;
|
|
|
|
|
2006-10-04 00:30:14 +00:00
|
|
|
if (data->isData)
|
|
|
|
{
|
|
|
|
char *ptr = XLogRecGetData(record) + sizeof(ginxlogSplit);
|
|
|
|
Size sizeofitem = GinSizeOfItem(lpage);
|
2006-05-02 11:28:56 +00:00
|
|
|
OffsetNumber i;
|
2006-10-04 00:30:14 +00:00
|
|
|
ItemPointer bound;
|
2006-05-02 11:28:56 +00:00
|
|
|
|
2006-10-04 00:30:14 +00:00
|
|
|
for (i = 0; i < data->separator; i++)
|
|
|
|
{
|
|
|
|
GinDataPageAddItem(lpage, ptr, InvalidOffsetNumber);
|
2006-05-02 11:28:56 +00:00
|
|
|
ptr += sizeofitem;
|
|
|
|
}
|
|
|
|
|
2006-10-04 00:30:14 +00:00
|
|
|
for (i = data->separator; i < data->nitem; i++)
|
|
|
|
{
|
|
|
|
GinDataPageAddItem(rpage, ptr, InvalidOffsetNumber);
|
2006-05-02 11:28:56 +00:00
|
|
|
ptr += sizeofitem;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* set up right key */
|
|
|
|
bound = GinDataPageGetRightBound(lpage);
|
2006-10-04 00:30:14 +00:00
|
|
|
if (data->isLeaf)
|
|
|
|
*bound = *(ItemPointerData *) GinDataPageGetItem(lpage, GinPageGetOpaque(lpage)->maxoff);
|
2006-05-02 11:28:56 +00:00
|
|
|
else
|
2006-10-04 00:30:14 +00:00
|
|
|
*bound = ((PostingItem *) GinDataPageGetItem(lpage, GinPageGetOpaque(lpage)->maxoff))->key;
|
2006-05-02 11:28:56 +00:00
|
|
|
|
|
|
|
bound = GinDataPageGetRightBound(rpage);
|
|
|
|
*bound = data->rightbound;
|
2006-10-04 00:30:14 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
IndexTuple itup = (IndexTuple) (XLogRecGetData(record) + sizeof(ginxlogSplit));
|
2006-05-02 11:28:56 +00:00
|
|
|
OffsetNumber i;
|
|
|
|
|
2006-10-04 00:30:14 +00:00
|
|
|
for (i = 0; i < data->separator; i++)
|
|
|
|
{
|
2007-09-20 17:56:33 +00:00
|
|
|
if (PageAddItem(lpage, (Item) itup, IndexTupleSize(itup), InvalidOffsetNumber, false, false) == InvalidOffsetNumber)
|
2006-10-04 00:30:14 +00:00
|
|
|
elog(ERROR, "failed to add item to index page in %u/%u/%u",
|
|
|
|
data->node.spcNode, data->node.dbNode, data->node.relNode);
|
|
|
|
itup = (IndexTuple) (((char *) itup) + MAXALIGN(IndexTupleSize(itup)));
|
2006-05-02 11:28:56 +00:00
|
|
|
}
|
|
|
|
|
2006-10-04 00:30:14 +00:00
|
|
|
for (i = data->separator; i < data->nitem; i++)
|
|
|
|
{
|
2007-09-20 17:56:33 +00:00
|
|
|
if (PageAddItem(rpage, (Item) itup, IndexTupleSize(itup), InvalidOffsetNumber, false, false) == InvalidOffsetNumber)
|
2006-10-04 00:30:14 +00:00
|
|
|
elog(ERROR, "failed to add item to index page in %u/%u/%u",
|
|
|
|
data->node.spcNode, data->node.dbNode, data->node.relNode);
|
|
|
|
itup = (IndexTuple) (((char *) itup) + MAXALIGN(IndexTupleSize(itup)));
|
2006-05-02 11:28:56 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
PageSetLSN(rpage, lsn);
|
2006-05-03 06:56:47 +00:00
|
|
|
PageSetTLI(rpage, ThisTimeLineID);
|
2006-05-02 11:28:56 +00:00
|
|
|
MarkBufferDirty(rbuffer);
|
|
|
|
|
|
|
|
PageSetLSN(lpage, lsn);
|
|
|
|
PageSetTLI(lpage, ThisTimeLineID);
|
|
|
|
MarkBufferDirty(lbuffer);
|
|
|
|
|
2006-10-04 00:30:14 +00:00
|
|
|
if (!data->isLeaf && data->updateBlkno != InvalidBlockNumber)
|
2006-05-02 11:28:56 +00:00
|
|
|
forgetIncompleteSplit(data->node, data->leftChildBlkno, data->updateBlkno);
|
|
|
|
|
2006-10-04 00:30:14 +00:00
|
|
|
if (data->isRootSplit)
|
|
|
|
{
|
2008-06-12 09:12:31 +00:00
|
|
|
Buffer rootBuf = XLogReadBuffer(data->node, data->rootBlkno, false);
|
2006-10-04 00:30:14 +00:00
|
|
|
Page rootPage = BufferGetPage(rootBuf);
|
2006-05-02 11:28:56 +00:00
|
|
|
|
2006-10-04 00:30:14 +00:00
|
|
|
GinInitBuffer(rootBuf, flags & ~GIN_LEAF);
|
2006-05-02 11:28:56 +00:00
|
|
|
|
2006-10-04 00:30:14 +00:00
|
|
|
if (data->isData)
|
|
|
|
{
|
|
|
|
Assert(data->rootBlkno != GIN_ROOT_BLKNO);
|
2006-05-02 11:28:56 +00:00
|
|
|
dataFillRoot(NULL, rootBuf, lbuffer, rbuffer);
|
2006-10-04 00:30:14 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Assert(data->rootBlkno == GIN_ROOT_BLKNO);
|
2006-05-02 11:28:56 +00:00
|
|
|
entryFillRoot(NULL, rootBuf, lbuffer, rbuffer);
|
|
|
|
}
|
|
|
|
|
|
|
|
PageSetLSN(rootPage, lsn);
|
|
|
|
PageSetTLI(rootPage, ThisTimeLineID);
|
|
|
|
|
|
|
|
MarkBufferDirty(rootBuf);
|
|
|
|
UnlockReleaseBuffer(rootBuf);
|
2006-10-04 00:30:14 +00:00
|
|
|
}
|
|
|
|
else
|
2006-05-02 11:28:56 +00:00
|
|
|
pushIncompleteSplit(data->node, data->lblkno, data->rblkno, data->rootBlkno);
|
|
|
|
|
|
|
|
UnlockReleaseBuffer(rbuffer);
|
|
|
|
UnlockReleaseBuffer(lbuffer);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2006-10-04 00:30:14 +00:00
|
|
|
ginRedoVacuumPage(XLogRecPtr lsn, XLogRecord *record)
|
|
|
|
{
|
|
|
|
ginxlogVacuumPage *data = (ginxlogVacuumPage *) XLogRecGetData(record);
|
2006-05-02 11:28:56 +00:00
|
|
|
Buffer buffer;
|
|
|
|
Page page;
|
|
|
|
|
|
|
|
/* nothing else to do if page was backed up (and no info to do it with) */
|
|
|
|
if (record->xl_info & XLR_BKP_BLOCK_1)
|
|
|
|
return;
|
|
|
|
|
2008-06-12 09:12:31 +00:00
|
|
|
buffer = XLogReadBuffer(data->node, data->blkno, false);
|
2006-05-02 11:28:56 +00:00
|
|
|
Assert(BufferIsValid(buffer));
|
|
|
|
page = (Page) BufferGetPage(buffer);
|
|
|
|
|
2006-10-04 00:30:14 +00:00
|
|
|
if (GinPageIsData(page))
|
|
|
|
{
|
|
|
|
memcpy(GinDataPageGetData(page), XLogRecGetData(record) + sizeof(ginxlogVacuumPage),
|
|
|
|
GinSizeOfItem(page) *data->nitem);
|
2006-05-02 11:28:56 +00:00
|
|
|
GinPageGetOpaque(page)->maxoff = data->nitem;
|
2006-10-04 00:30:14 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
OffsetNumber i,
|
|
|
|
*tod;
|
|
|
|
IndexTuple itup = (IndexTuple) (XLogRecGetData(record) + sizeof(ginxlogVacuumPage));
|
|
|
|
|
|
|
|
tod = (OffsetNumber *) palloc(sizeof(OffsetNumber) * PageGetMaxOffsetNumber(page));
|
|
|
|
for (i = FirstOffsetNumber; i <= PageGetMaxOffsetNumber(page); i++)
|
|
|
|
tod[i - 1] = i;
|
|
|
|
|
|
|
|
PageIndexMultiDelete(page, tod, PageGetMaxOffsetNumber(page));
|
|
|
|
|
|
|
|
for (i = 0; i < data->nitem; i++)
|
|
|
|
{
|
2007-09-20 17:56:33 +00:00
|
|
|
if (PageAddItem(page, (Item) itup, IndexTupleSize(itup), InvalidOffsetNumber, false, false) == InvalidOffsetNumber)
|
2006-10-04 00:30:14 +00:00
|
|
|
elog(ERROR, "failed to add item to index page in %u/%u/%u",
|
|
|
|
data->node.spcNode, data->node.dbNode, data->node.relNode);
|
|
|
|
itup = (IndexTuple) (((char *) itup) + MAXALIGN(IndexTupleSize(itup)));
|
2006-05-02 11:28:56 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
PageSetLSN(page, lsn);
|
|
|
|
PageSetTLI(page, ThisTimeLineID);
|
|
|
|
|
|
|
|
MarkBufferDirty(buffer);
|
|
|
|
UnlockReleaseBuffer(buffer);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2006-10-04 00:30:14 +00:00
|
|
|
ginRedoDeletePage(XLogRecPtr lsn, XLogRecord *record)
|
|
|
|
{
|
|
|
|
ginxlogDeletePage *data = (ginxlogDeletePage *) XLogRecGetData(record);
|
2006-05-02 11:28:56 +00:00
|
|
|
Buffer buffer;
|
|
|
|
Page page;
|
|
|
|
|
2006-10-04 00:30:14 +00:00
|
|
|
if (!(record->xl_info & XLR_BKP_BLOCK_1))
|
|
|
|
{
|
2008-06-12 09:12:31 +00:00
|
|
|
buffer = XLogReadBuffer(data->node, data->blkno, false);
|
2006-10-04 00:30:14 +00:00
|
|
|
page = BufferGetPage(buffer);
|
2006-05-02 11:28:56 +00:00
|
|
|
Assert(GinPageIsData(page));
|
|
|
|
GinPageGetOpaque(page)->flags = GIN_DELETED;
|
|
|
|
PageSetLSN(page, lsn);
|
|
|
|
PageSetTLI(page, ThisTimeLineID);
|
|
|
|
MarkBufferDirty(buffer);
|
|
|
|
UnlockReleaseBuffer(buffer);
|
|
|
|
}
|
|
|
|
|
2006-10-04 00:30:14 +00:00
|
|
|
if (!(record->xl_info & XLR_BKP_BLOCK_2))
|
|
|
|
{
|
2008-06-12 09:12:31 +00:00
|
|
|
buffer = XLogReadBuffer(data->node, data->parentBlkno, false);
|
2006-10-04 00:30:14 +00:00
|
|
|
page = BufferGetPage(buffer);
|
2006-05-02 11:28:56 +00:00
|
|
|
Assert(GinPageIsData(page));
|
|
|
|
Assert(!GinPageIsLeaf(page));
|
|
|
|
PageDeletePostingItem(page, data->parentOffset);
|
|
|
|
PageSetLSN(page, lsn);
|
|
|
|
PageSetTLI(page, ThisTimeLineID);
|
|
|
|
MarkBufferDirty(buffer);
|
|
|
|
UnlockReleaseBuffer(buffer);
|
|
|
|
}
|
|
|
|
|
2007-06-04 15:56:28 +00:00
|
|
|
if (!(record->xl_info & XLR_BKP_BLOCK_3) && data->leftBlkno != InvalidBlockNumber)
|
2006-10-04 00:30:14 +00:00
|
|
|
{
|
2008-06-12 09:12:31 +00:00
|
|
|
buffer = XLogReadBuffer(data->node, data->leftBlkno, false);
|
2006-10-04 00:30:14 +00:00
|
|
|
page = BufferGetPage(buffer);
|
2006-05-02 11:28:56 +00:00
|
|
|
Assert(GinPageIsData(page));
|
|
|
|
GinPageGetOpaque(page)->rightlink = data->rightLink;
|
|
|
|
PageSetLSN(page, lsn);
|
|
|
|
PageSetTLI(page, ThisTimeLineID);
|
|
|
|
MarkBufferDirty(buffer);
|
|
|
|
UnlockReleaseBuffer(buffer);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-03-24 20:17:18 +00:00
|
|
|
static void
|
|
|
|
ginRedoUpdateMetapage(XLogRecPtr lsn, XLogRecord *record)
|
|
|
|
{
|
2009-06-11 14:49:15 +00:00
|
|
|
ginxlogUpdateMeta *data = (ginxlogUpdateMeta *) XLogRecGetData(record);
|
2009-03-24 20:17:18 +00:00
|
|
|
Buffer metabuffer;
|
|
|
|
Page metapage;
|
|
|
|
|
|
|
|
metabuffer = XLogReadBuffer(data->node, GIN_METAPAGE_BLKNO, false);
|
|
|
|
metapage = BufferGetPage(metabuffer);
|
|
|
|
|
|
|
|
if (!XLByteLE(lsn, PageGetLSN(metapage)))
|
|
|
|
{
|
2009-06-11 14:49:15 +00:00
|
|
|
memcpy(GinPageGetMeta(metapage), &data->metadata, sizeof(GinMetaPageData));
|
2009-03-24 20:17:18 +00:00
|
|
|
PageSetLSN(metapage, lsn);
|
|
|
|
PageSetTLI(metapage, ThisTimeLineID);
|
|
|
|
MarkBufferDirty(metabuffer);
|
|
|
|
}
|
|
|
|
|
2009-06-11 14:49:15 +00:00
|
|
|
if (data->ntuples > 0)
|
2009-03-24 20:17:18 +00:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
* insert into tail page
|
|
|
|
*/
|
|
|
|
if (!(record->xl_info & XLR_BKP_BLOCK_1))
|
|
|
|
{
|
2009-06-11 14:49:15 +00:00
|
|
|
Buffer buffer = XLogReadBuffer(data->node, data->metadata.tail, false);
|
|
|
|
Page page = BufferGetPage(buffer);
|
2009-03-24 20:17:18 +00:00
|
|
|
|
2009-06-11 14:49:15 +00:00
|
|
|
if (!XLByteLE(lsn, PageGetLSN(page)))
|
2009-03-24 20:17:18 +00:00
|
|
|
{
|
2009-06-11 14:49:15 +00:00
|
|
|
OffsetNumber l,
|
|
|
|
off = (PageIsEmpty(page)) ? FirstOffsetNumber :
|
|
|
|
OffsetNumberNext(PageGetMaxOffsetNumber(page));
|
|
|
|
int i,
|
|
|
|
tupsize;
|
|
|
|
IndexTuple tuples = (IndexTuple) (XLogRecGetData(record) + sizeof(ginxlogUpdateMeta));
|
|
|
|
|
|
|
|
for (i = 0; i < data->ntuples; i++)
|
2009-03-24 20:17:18 +00:00
|
|
|
{
|
|
|
|
tupsize = IndexTupleSize(tuples);
|
|
|
|
|
2009-06-11 14:49:15 +00:00
|
|
|
l = PageAddItem(page, (Item) tuples, tupsize, off, false, false);
|
2009-03-24 20:17:18 +00:00
|
|
|
|
|
|
|
if (l == InvalidOffsetNumber)
|
|
|
|
elog(ERROR, "failed to add item to index page");
|
|
|
|
|
2009-06-11 14:49:15 +00:00
|
|
|
tuples = (IndexTuple) (((char *) tuples) + tupsize);
|
2009-03-24 20:17:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Increase counter of heap tuples
|
|
|
|
*/
|
|
|
|
GinPageGetOpaque(page)->maxoff++;
|
|
|
|
|
|
|
|
PageSetLSN(page, lsn);
|
|
|
|
PageSetTLI(page, ThisTimeLineID);
|
|
|
|
MarkBufferDirty(buffer);
|
|
|
|
}
|
|
|
|
UnlockReleaseBuffer(buffer);
|
|
|
|
}
|
|
|
|
}
|
2009-06-11 14:49:15 +00:00
|
|
|
else if (data->prevTail != InvalidBlockNumber)
|
2009-03-24 20:17:18 +00:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
* New tail
|
|
|
|
*/
|
|
|
|
|
2009-06-11 14:49:15 +00:00
|
|
|
Buffer buffer = XLogReadBuffer(data->node, data->prevTail, false);
|
|
|
|
Page page = BufferGetPage(buffer);
|
2009-03-24 20:17:18 +00:00
|
|
|
|
2009-06-11 14:49:15 +00:00
|
|
|
if (!XLByteLE(lsn, PageGetLSN(page)))
|
2009-03-24 20:17:18 +00:00
|
|
|
{
|
|
|
|
GinPageGetOpaque(page)->rightlink = data->newRightlink;
|
|
|
|
|
|
|
|
PageSetLSN(page, lsn);
|
|
|
|
PageSetTLI(page, ThisTimeLineID);
|
|
|
|
MarkBufferDirty(buffer);
|
|
|
|
}
|
|
|
|
UnlockReleaseBuffer(buffer);
|
|
|
|
}
|
|
|
|
|
|
|
|
UnlockReleaseBuffer(metabuffer);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
ginRedoInsertListPage(XLogRecPtr lsn, XLogRecord *record)
|
|
|
|
{
|
2009-06-11 14:49:15 +00:00
|
|
|
ginxlogInsertListPage *data = (ginxlogInsertListPage *) XLogRecGetData(record);
|
|
|
|
Buffer buffer;
|
|
|
|
Page page;
|
|
|
|
OffsetNumber l,
|
|
|
|
off = FirstOffsetNumber;
|
|
|
|
int i,
|
|
|
|
tupsize;
|
|
|
|
IndexTuple tuples = (IndexTuple) (XLogRecGetData(record) + sizeof(ginxlogInsertListPage));
|
2009-03-24 20:17:18 +00:00
|
|
|
|
|
|
|
if (record->xl_info & XLR_BKP_BLOCK_1)
|
|
|
|
return;
|
|
|
|
|
|
|
|
buffer = XLogReadBuffer(data->node, data->blkno, true);
|
|
|
|
page = BufferGetPage(buffer);
|
|
|
|
|
|
|
|
GinInitBuffer(buffer, GIN_LIST);
|
|
|
|
GinPageGetOpaque(page)->rightlink = data->rightlink;
|
2009-06-11 14:49:15 +00:00
|
|
|
if (data->rightlink == InvalidBlockNumber)
|
2009-03-24 20:17:18 +00:00
|
|
|
{
|
|
|
|
/* tail of sublist */
|
|
|
|
GinPageSetFullRow(page);
|
|
|
|
GinPageGetOpaque(page)->maxoff = 1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
GinPageGetOpaque(page)->maxoff = 0;
|
|
|
|
}
|
|
|
|
|
2009-06-11 14:49:15 +00:00
|
|
|
for (i = 0; i < data->ntuples; i++)
|
2009-03-24 20:17:18 +00:00
|
|
|
{
|
|
|
|
tupsize = IndexTupleSize(tuples);
|
|
|
|
|
2009-06-11 14:49:15 +00:00
|
|
|
l = PageAddItem(page, (Item) tuples, tupsize, off, false, false);
|
2009-03-24 20:17:18 +00:00
|
|
|
|
|
|
|
if (l == InvalidOffsetNumber)
|
|
|
|
elog(ERROR, "failed to add item to index page");
|
|
|
|
|
2009-06-11 14:49:15 +00:00
|
|
|
tuples = (IndexTuple) (((char *) tuples) + tupsize);
|
2009-03-24 20:17:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
PageSetLSN(page, lsn);
|
|
|
|
PageSetTLI(page, ThisTimeLineID);
|
|
|
|
MarkBufferDirty(buffer);
|
|
|
|
|
|
|
|
UnlockReleaseBuffer(buffer);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
ginRedoDeleteListPages(XLogRecPtr lsn, XLogRecord *record)
|
|
|
|
{
|
2009-06-11 14:49:15 +00:00
|
|
|
ginxlogDeleteListPages *data = (ginxlogDeleteListPages *) XLogRecGetData(record);
|
2009-03-24 20:17:18 +00:00
|
|
|
Buffer metabuffer;
|
|
|
|
Page metapage;
|
2009-06-11 14:49:15 +00:00
|
|
|
int i;
|
2009-03-24 20:17:18 +00:00
|
|
|
|
|
|
|
metabuffer = XLogReadBuffer(data->node, GIN_METAPAGE_BLKNO, false);
|
|
|
|
metapage = BufferGetPage(metabuffer);
|
|
|
|
|
|
|
|
if (!XLByteLE(lsn, PageGetLSN(metapage)))
|
|
|
|
{
|
2009-06-11 14:49:15 +00:00
|
|
|
memcpy(GinPageGetMeta(metapage), &data->metadata, sizeof(GinMetaPageData));
|
2009-03-24 20:17:18 +00:00
|
|
|
PageSetLSN(metapage, lsn);
|
|
|
|
PageSetTLI(metapage, ThisTimeLineID);
|
|
|
|
MarkBufferDirty(metabuffer);
|
|
|
|
}
|
|
|
|
|
2009-06-11 14:49:15 +00:00
|
|
|
for (i = 0; i < data->ndeleted; i++)
|
2009-03-24 20:17:18 +00:00
|
|
|
{
|
2009-06-11 14:49:15 +00:00
|
|
|
Buffer buffer = XLogReadBuffer(data->node, data->toDelete[i], false);
|
|
|
|
Page page = BufferGetPage(buffer);
|
2009-03-24 20:17:18 +00:00
|
|
|
|
2009-06-11 14:49:15 +00:00
|
|
|
if (!XLByteLE(lsn, PageGetLSN(page)))
|
2009-03-24 20:17:18 +00:00
|
|
|
{
|
|
|
|
GinPageGetOpaque(page)->flags = GIN_DELETED;
|
|
|
|
|
|
|
|
PageSetLSN(page, lsn);
|
|
|
|
PageSetTLI(page, ThisTimeLineID);
|
|
|
|
MarkBufferDirty(buffer);
|
|
|
|
}
|
|
|
|
|
|
|
|
UnlockReleaseBuffer(buffer);
|
|
|
|
}
|
|
|
|
UnlockReleaseBuffer(metabuffer);
|
|
|
|
}
|
|
|
|
|
2006-10-04 00:30:14 +00:00
|
|
|
void
|
|
|
|
gin_redo(XLogRecPtr lsn, XLogRecord *record)
|
|
|
|
{
|
|
|
|
uint8 info = record->xl_info & ~XLR_INFO_MASK;
|
2006-05-02 11:28:56 +00:00
|
|
|
|
Allow read only connections during recovery, known as Hot Standby.
Enabled by recovery_connections = on (default) and forcing archive recovery using a recovery.conf. Recovery processing now emulates the original transactions as they are replayed, providing full locking and MVCC behaviour for read only queries. Recovery must enter consistent state before connections are allowed, so there is a delay, typically short, before connections succeed. Replay of recovering transactions can conflict and in some cases deadlock with queries during recovery; these result in query cancellation after max_standby_delay seconds have expired. Infrastructure changes have minor effects on normal running, though introduce four new types of WAL record.
New test mode "make standbycheck" allows regression tests of static command behaviour on a standby server while in recovery. Typical and extreme dynamic behaviours have been checked via code inspection and manual testing. Few port specific behaviours have been utilised, though primary testing has been on Linux only so far.
This commit is the basic patch. Additional changes will follow in this release to enhance some aspects of behaviour, notably improved handling of conflicts, deadlock detection and query cancellation. Changes to VACUUM FULL are also required.
Simon Riggs, with significant and lengthy review by Heikki Linnakangas, including streamlined redesign of snapshot creation and two-phase commit.
Important contributions from Florian Pflug, Mark Kirkwood, Merlin Moncure, Greg Stark, Gianni Ciolli, Gabriele Bartolini, Hannu Krosing, Robert Haas, Tatsuo Ishii, Hiroyuki Yamada plus support and feedback from many other community members.
2009-12-19 01:32:45 +00:00
|
|
|
/*
|
|
|
|
* GIN indexes do not require any conflict processing.
|
|
|
|
*/
|
|
|
|
|
2009-01-20 18:59:37 +00:00
|
|
|
RestoreBkpBlocks(lsn, record, false);
|
|
|
|
|
2006-05-02 11:28:56 +00:00
|
|
|
topCtx = MemoryContextSwitchTo(opCtx);
|
2006-10-04 00:30:14 +00:00
|
|
|
switch (info)
|
|
|
|
{
|
|
|
|
case XLOG_GIN_CREATE_INDEX:
|
2006-05-02 11:28:56 +00:00
|
|
|
ginRedoCreateIndex(lsn, record);
|
|
|
|
break;
|
2006-10-04 00:30:14 +00:00
|
|
|
case XLOG_GIN_CREATE_PTREE:
|
2006-05-02 11:28:56 +00:00
|
|
|
ginRedoCreatePTree(lsn, record);
|
|
|
|
break;
|
2006-10-04 00:30:14 +00:00
|
|
|
case XLOG_GIN_INSERT:
|
2006-05-02 11:28:56 +00:00
|
|
|
ginRedoInsert(lsn, record);
|
|
|
|
break;
|
2006-10-04 00:30:14 +00:00
|
|
|
case XLOG_GIN_SPLIT:
|
2006-05-02 11:28:56 +00:00
|
|
|
ginRedoSplit(lsn, record);
|
|
|
|
break;
|
2006-10-04 00:30:14 +00:00
|
|
|
case XLOG_GIN_VACUUM_PAGE:
|
2006-05-02 11:28:56 +00:00
|
|
|
ginRedoVacuumPage(lsn, record);
|
|
|
|
break;
|
2006-10-04 00:30:14 +00:00
|
|
|
case XLOG_GIN_DELETE_PAGE:
|
2006-05-02 11:28:56 +00:00
|
|
|
ginRedoDeletePage(lsn, record);
|
|
|
|
break;
|
2009-03-24 20:17:18 +00:00
|
|
|
case XLOG_GIN_UPDATE_META_PAGE:
|
|
|
|
ginRedoUpdateMetapage(lsn, record);
|
|
|
|
break;
|
|
|
|
case XLOG_GIN_INSERT_LISTPAGE:
|
|
|
|
ginRedoInsertListPage(lsn, record);
|
|
|
|
break;
|
2009-06-11 14:49:15 +00:00
|
|
|
case XLOG_GIN_DELETE_LISTPAGE:
|
2009-03-24 20:17:18 +00:00
|
|
|
ginRedoDeleteListPages(lsn, record);
|
|
|
|
break;
|
2006-05-02 11:28:56 +00:00
|
|
|
default:
|
|
|
|
elog(PANIC, "gin_redo: unknown op code %u", info);
|
|
|
|
}
|
|
|
|
MemoryContextSwitchTo(topCtx);
|
|
|
|
MemoryContextReset(opCtx);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2006-10-04 00:30:14 +00:00
|
|
|
desc_node(StringInfo buf, RelFileNode node, BlockNumber blkno)
|
|
|
|
{
|
|
|
|
appendStringInfo(buf, "node: %u/%u/%u blkno: %u",
|
|
|
|
node.spcNode, node.dbNode, node.relNode, blkno);
|
2006-05-02 11:28:56 +00:00
|
|
|
}
|
|
|
|
|
2006-10-04 00:30:14 +00:00
|
|
|
void
|
|
|
|
gin_desc(StringInfo buf, uint8 xl_info, char *rec)
|
|
|
|
{
|
|
|
|
uint8 info = xl_info & ~XLR_INFO_MASK;
|
2006-05-02 11:28:56 +00:00
|
|
|
|
2006-10-04 00:30:14 +00:00
|
|
|
switch (info)
|
|
|
|
{
|
|
|
|
case XLOG_GIN_CREATE_INDEX:
|
|
|
|
appendStringInfo(buf, "Create index, ");
|
|
|
|
desc_node(buf, *(RelFileNode *) rec, GIN_ROOT_BLKNO);
|
2006-05-02 11:28:56 +00:00
|
|
|
break;
|
2006-10-04 00:30:14 +00:00
|
|
|
case XLOG_GIN_CREATE_PTREE:
|
|
|
|
appendStringInfo(buf, "Create posting tree, ");
|
|
|
|
desc_node(buf, ((ginxlogCreatePostingTree *) rec)->node, ((ginxlogCreatePostingTree *) rec)->blkno);
|
2006-05-02 11:28:56 +00:00
|
|
|
break;
|
2006-10-04 00:30:14 +00:00
|
|
|
case XLOG_GIN_INSERT:
|
|
|
|
appendStringInfo(buf, "Insert item, ");
|
|
|
|
desc_node(buf, ((ginxlogInsert *) rec)->node, ((ginxlogInsert *) rec)->blkno);
|
|
|
|
appendStringInfo(buf, " offset: %u nitem: %u isdata: %c isleaf %c isdelete %c updateBlkno:%u",
|
|
|
|
((ginxlogInsert *) rec)->offset,
|
|
|
|
((ginxlogInsert *) rec)->nitem,
|
|
|
|
(((ginxlogInsert *) rec)->isData) ? 'T' : 'F',
|
|
|
|
(((ginxlogInsert *) rec)->isLeaf) ? 'T' : 'F',
|
|
|
|
(((ginxlogInsert *) rec)->isDelete) ? 'T' : 'F',
|
|
|
|
((ginxlogInsert *) rec)->updateBlkno
|
|
|
|
);
|
2006-05-02 11:28:56 +00:00
|
|
|
|
|
|
|
break;
|
2006-10-04 00:30:14 +00:00
|
|
|
case XLOG_GIN_SPLIT:
|
|
|
|
appendStringInfo(buf, "Page split, ");
|
|
|
|
desc_node(buf, ((ginxlogSplit *) rec)->node, ((ginxlogSplit *) rec)->lblkno);
|
|
|
|
appendStringInfo(buf, " isrootsplit: %c", (((ginxlogSplit *) rec)->isRootSplit) ? 'T' : 'F');
|
2006-05-02 11:28:56 +00:00
|
|
|
break;
|
2006-10-04 00:30:14 +00:00
|
|
|
case XLOG_GIN_VACUUM_PAGE:
|
|
|
|
appendStringInfo(buf, "Vacuum page, ");
|
|
|
|
desc_node(buf, ((ginxlogVacuumPage *) rec)->node, ((ginxlogVacuumPage *) rec)->blkno);
|
2006-05-02 11:28:56 +00:00
|
|
|
break;
|
2006-10-04 00:30:14 +00:00
|
|
|
case XLOG_GIN_DELETE_PAGE:
|
|
|
|
appendStringInfo(buf, "Delete page, ");
|
|
|
|
desc_node(buf, ((ginxlogDeletePage *) rec)->node, ((ginxlogDeletePage *) rec)->blkno);
|
2006-05-02 11:28:56 +00:00
|
|
|
break;
|
2009-03-24 20:17:18 +00:00
|
|
|
case XLOG_GIN_UPDATE_META_PAGE:
|
|
|
|
appendStringInfo(buf, "Update metapage, ");
|
2009-06-11 14:49:15 +00:00
|
|
|
desc_node(buf, ((ginxlogUpdateMeta *) rec)->node, ((ginxlogUpdateMeta *) rec)->metadata.tail);
|
2009-03-24 20:17:18 +00:00
|
|
|
break;
|
|
|
|
case XLOG_GIN_INSERT_LISTPAGE:
|
|
|
|
appendStringInfo(buf, "Insert new list page, ");
|
2009-06-11 14:49:15 +00:00
|
|
|
desc_node(buf, ((ginxlogInsertListPage *) rec)->node, ((ginxlogInsertListPage *) rec)->blkno);
|
2009-03-24 20:17:18 +00:00
|
|
|
break;
|
|
|
|
case XLOG_GIN_DELETE_LISTPAGE:
|
|
|
|
appendStringInfo(buf, "Delete list pages (%d), ", ((ginxlogDeleteListPages *) rec)->ndeleted);
|
2009-06-11 14:49:15 +00:00
|
|
|
desc_node(buf, ((ginxlogDeleteListPages *) rec)->node, ((ginxlogDeleteListPages *) rec)->metadata.head);
|
2009-03-24 20:17:18 +00:00
|
|
|
break;
|
2006-05-02 11:28:56 +00:00
|
|
|
default:
|
|
|
|
elog(PANIC, "gin_desc: unknown op code %u", info);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-10-04 00:30:14 +00:00
|
|
|
void
|
|
|
|
gin_xlog_startup(void)
|
|
|
|
{
|
2006-05-02 11:28:56 +00:00
|
|
|
incomplete_splits = NIL;
|
|
|
|
|
|
|
|
opCtx = AllocSetContextCreate(CurrentMemoryContext,
|
2006-10-04 00:30:14 +00:00
|
|
|
"GIN recovery temporary context",
|
|
|
|
ALLOCSET_DEFAULT_MINSIZE,
|
|
|
|
ALLOCSET_DEFAULT_INITSIZE,
|
|
|
|
ALLOCSET_DEFAULT_MAXSIZE);
|
2006-05-02 11:28:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2006-10-04 00:30:14 +00:00
|
|
|
ginContinueSplit(ginIncompleteSplit *split)
|
|
|
|
{
|
2006-05-02 11:28:56 +00:00
|
|
|
GinBtreeData btree;
|
|
|
|
Relation reln;
|
|
|
|
Buffer buffer;
|
2006-10-04 00:30:14 +00:00
|
|
|
GinBtreeStack stack;
|
2006-05-02 11:28:56 +00:00
|
|
|
|
2006-10-04 00:30:14 +00:00
|
|
|
/*
|
|
|
|
* elog(NOTICE,"ginContinueSplit root:%u l:%u r:%u", split->rootBlkno,
|
|
|
|
* split->leftBlkno, split->rightBlkno);
|
|
|
|
*/
|
2008-06-12 09:12:31 +00:00
|
|
|
buffer = XLogReadBuffer(split->node, split->leftBlkno, false);
|
2006-05-02 11:28:56 +00:00
|
|
|
|
2008-06-12 09:12:31 +00:00
|
|
|
reln = CreateFakeRelcacheEntry(split->node);
|
2006-05-02 11:28:56 +00:00
|
|
|
|
2006-10-04 00:30:14 +00:00
|
|
|
if (split->rootBlkno == GIN_ROOT_BLKNO)
|
|
|
|
{
|
2008-07-11 21:06:29 +00:00
|
|
|
prepareEntryScan(&btree, reln, InvalidOffsetNumber, (Datum) 0, NULL);
|
2006-10-04 00:30:14 +00:00
|
|
|
btree.entry = ginPageGetLinkItup(buffer);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Page page = BufferGetPage(buffer);
|
2006-05-02 11:28:56 +00:00
|
|
|
|
2006-10-04 00:30:14 +00:00
|
|
|
prepareDataScan(&btree, reln);
|
2006-05-02 11:28:56 +00:00
|
|
|
|
2006-10-04 00:30:14 +00:00
|
|
|
PostingItemSetBlockNumber(&(btree.pitem), split->leftBlkno);
|
|
|
|
if (GinPageIsLeaf(page))
|
|
|
|
btree.pitem.key = *(ItemPointerData *) GinDataPageGetItem(page,
|
|
|
|
GinPageGetOpaque(page)->maxoff);
|
2006-05-02 11:28:56 +00:00
|
|
|
else
|
2006-10-04 00:30:14 +00:00
|
|
|
btree.pitem.key = ((PostingItem *) GinDataPageGetItem(page,
|
|
|
|
GinPageGetOpaque(page)->maxoff))->key;
|
2006-05-02 11:28:56 +00:00
|
|
|
}
|
|
|
|
|
2006-10-04 00:30:14 +00:00
|
|
|
btree.rightblkno = split->rightBlkno;
|
2006-05-02 11:28:56 +00:00
|
|
|
|
|
|
|
stack.blkno = split->leftBlkno;
|
|
|
|
stack.buffer = buffer;
|
|
|
|
stack.off = InvalidOffsetNumber;
|
|
|
|
stack.parent = NULL;
|
|
|
|
|
2006-10-04 00:30:14 +00:00
|
|
|
findParents(&btree, &stack, split->rootBlkno);
|
|
|
|
ginInsertValue(&btree, stack.parent);
|
2006-05-02 11:28:56 +00:00
|
|
|
|
2010-02-09 20:31:24 +00:00
|
|
|
FreeFakeRelcacheEntry(reln);
|
|
|
|
|
2006-10-04 00:30:14 +00:00
|
|
|
UnlockReleaseBuffer(buffer);
|
2006-05-02 11:28:56 +00:00
|
|
|
}
|
|
|
|
|
2006-10-04 00:30:14 +00:00
|
|
|
void
|
|
|
|
gin_xlog_cleanup(void)
|
|
|
|
{
|
2006-05-02 11:28:56 +00:00
|
|
|
ListCell *l;
|
|
|
|
MemoryContext topCtx;
|
|
|
|
|
|
|
|
topCtx = MemoryContextSwitchTo(opCtx);
|
|
|
|
|
2006-08-07 16:57:57 +00:00
|
|
|
foreach(l, incomplete_splits)
|
|
|
|
{
|
2006-05-02 11:28:56 +00:00
|
|
|
ginIncompleteSplit *split = (ginIncompleteSplit *) lfirst(l);
|
2006-10-04 00:30:14 +00:00
|
|
|
|
|
|
|
ginContinueSplit(split);
|
|
|
|
MemoryContextReset(opCtx);
|
2006-05-02 11:28:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
MemoryContextSwitchTo(topCtx);
|
|
|
|
MemoryContextDelete(opCtx);
|
2007-06-04 15:56:28 +00:00
|
|
|
incomplete_splits = NIL;
|
2006-05-02 11:28:56 +00:00
|
|
|
}
|
|
|
|
|
2006-08-07 16:57:57 +00:00
|
|
|
bool
|
|
|
|
gin_safe_restartpoint(void)
|
|
|
|
{
|
|
|
|
if (incomplete_splits)
|
|
|
|
return false;
|
|
|
|
return true;
|
|
|
|
}
|