forked from huawei/openGauss-server
upgrade and parallel xlog for hash index
This commit is contained in:
parent
1df3aad88d
commit
27e85b6e16
|
|
@ -13,6 +13,7 @@
|
|||
#include "access/gin.h"
|
||||
#include "access/gist_private.h"
|
||||
#include "access/hash.h"
|
||||
#include "access/hash_xlog.h"
|
||||
#include "access/heapam.h"
|
||||
#include "access/multixact.h"
|
||||
#include "access/nbtree.h"
|
||||
|
|
|
|||
|
|
@ -363,7 +363,6 @@ static void pgstat_hash_page(pgstattuple_type* stat, Relation rel, BlockNumber b
|
|||
Page page;
|
||||
OffsetNumber maxoff;
|
||||
|
||||
_hash_getlock(rel, blkno, HASH_SHARE);
|
||||
buf = _hash_getbuf_with_strategy(rel, blkno, HASH_READ, 0, bstrategy);
|
||||
page = BufferGetPage(buf);
|
||||
|
||||
|
|
@ -390,7 +389,6 @@ static void pgstat_hash_page(pgstattuple_type* stat, Relation rel, BlockNumber b
|
|||
}
|
||||
|
||||
_hash_relbuf(rel, buf);
|
||||
_hash_droplock(rel, blkno, HASH_SHARE);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -3353,12 +3353,21 @@ IndexStmt* transformIndexStmt(Oid relid, IndexStmt* stmt, const char* queryStrin
|
|||
|
||||
if (!isColStore && (0 != pg_strcasecmp(stmt->accessMethod, DEFAULT_INDEX_TYPE)) &&
|
||||
(0 != pg_strcasecmp(stmt->accessMethod, DEFAULT_GIN_INDEX_TYPE)) &&
|
||||
(0 != pg_strcasecmp(stmt->accessMethod, DEFAULT_GIST_INDEX_TYPE))) {
|
||||
/* row store only support btree/gin/gist index */
|
||||
(0 != pg_strcasecmp(stmt->accessMethod, DEFAULT_GIST_INDEX_TYPE)) &&
|
||||
(0 != pg_strcasecmp(stmt->accessMethod, DEFAULT_HASH_INDEX_TYPE))) {
|
||||
/* row store only support btree/gin/gist/hash index */
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("access method \"%s\" does not support row store", stmt->accessMethod)));
|
||||
}
|
||||
|
||||
if (0 == pg_strcasecmp(stmt->accessMethod, DEFAULT_HASH_INDEX_TYPE) &&
|
||||
t_thrd.proc->workingVersionNum < SUPPORT_HASH_XLOG_VERSION_NUM) {
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("access method \"%s\" does not support row store", stmt->accessMethod)));
|
||||
}
|
||||
|
||||
if (isColStore && (!isPsortMothed && !isCBtreeMethod && !isCGinBtreeMethod)) {
|
||||
/* column store support psort/cbtree/gin index */
|
||||
ereport(ERROR,
|
||||
|
|
|
|||
|
|
@ -79,6 +79,7 @@ const uint32 ML_OPT_MODEL_VERSION_NUM = 92284;
|
|||
const uint32 FIX_SQL_ADD_RELATION_REF_COUNT = 92291;
|
||||
const uint32 GENERATED_COL_VERSION_NUM = 92303;
|
||||
const uint32 ANALYZER_HOOK_VERSION_NUM = 92306;
|
||||
const uint32 SUPPORT_HASH_XLOG_VERSION_NUM = 92304;
|
||||
|
||||
/* This variable indicates wheather the instance is in progress of upgrade as a whole */
|
||||
uint32 volatile WorkingGrandVersionNum = GRAND_VERSION_NUM;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,865 @@
|
|||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* hash_xlog.cpp
|
||||
* WAL replay logic for hash index.
|
||||
*
|
||||
* Portions Copyright (c) 2021 Huawei Technologies Co.,Ltd.
|
||||
* Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* src/gausskernel/storage/access/hash/hash_xlog.cpp
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include "access/xlogproc.h"
|
||||
#include "access/hash.h"
|
||||
#include "access/hash_xlog.h"
|
||||
#include "access/xlogutils.h"
|
||||
#include "access/xlog.h"
|
||||
#include "access/transam.h"
|
||||
#include "access/xlogproc.h"
|
||||
#include "storage/procarray.h"
|
||||
#include "miscadmin.h"
|
||||
|
||||
/*
|
||||
* replay a hash index meta page
|
||||
*/
|
||||
static void hash_xlog_init_meta_page(XLogReaderState *record)
|
||||
{
|
||||
RedoBufferInfo metabuf;
|
||||
ForkNumber forknum;
|
||||
|
||||
/* create the index' metapage */
|
||||
XLogInitBufferForRedo(record, 0, &metabuf);
|
||||
Assert(BufferIsValid(metabuf.buf));
|
||||
HashRedoInitMetaPageOperatorPage(&metabuf, XLogRecGetData(record));
|
||||
MarkBufferDirty(metabuf.buf);
|
||||
|
||||
/*
|
||||
* Force the on-disk state of init forks to always be in sync with the
|
||||
* state in shared buffers. See XLogReadBufferForRedoExtended. We need
|
||||
* special handling for init forks as create index operations don't log a
|
||||
* full page image of the metapage.
|
||||
*/
|
||||
XLogRecGetBlockTag(record, 0, NULL, &forknum, NULL);
|
||||
if (forknum == INIT_FORKNUM)
|
||||
FlushOneBuffer(metabuf.buf);
|
||||
|
||||
/* all done */
|
||||
UnlockReleaseBuffer(metabuf.buf);
|
||||
}
|
||||
|
||||
/*
|
||||
* replay a hash index bitmap page
|
||||
*/
|
||||
static void hash_xlog_init_bitmap_page(XLogReaderState *record)
|
||||
{
|
||||
RedoBufferInfo bitmapbuf;
|
||||
RedoBufferInfo metabuf;
|
||||
ForkNumber forknum;
|
||||
|
||||
/*
|
||||
* Initialize bitmap page
|
||||
*/
|
||||
XLogInitBufferForRedo(record, 0, &bitmapbuf);
|
||||
HashRedoInitBitmapPageOperatorBitmapPage(&bitmapbuf, XLogRecGetData(record));
|
||||
MarkBufferDirty(bitmapbuf.buf);
|
||||
|
||||
/*
|
||||
* Force the on-disk state of init forks to always be in sync with the
|
||||
* state in shared buffers. See XLogReadBufferForRedoExtended. We need
|
||||
* special handling for init forks as create index operations don't log a
|
||||
* full page image of the metapage.
|
||||
*/
|
||||
XLogRecGetBlockTag(record, 0, NULL, &forknum, NULL);
|
||||
if (forknum == INIT_FORKNUM)
|
||||
FlushOneBuffer(bitmapbuf.buf);
|
||||
UnlockReleaseBuffer(bitmapbuf.buf);
|
||||
|
||||
/* add the new bitmap page to the metapage's list of bitmaps */
|
||||
if (XLogReadBufferForRedo(record, 1, &metabuf) == BLK_NEEDS_REDO) {
|
||||
/*
|
||||
* Note: in normal operation, we'd update the metapage while still
|
||||
* holding lock on the bitmap page. But during replay it's not
|
||||
* necessary to hold that lock, since nobody can see it yet; the
|
||||
* creating transaction hasn't yet committed.
|
||||
*/
|
||||
HashRedoInitBitmapPageOperatorMetaPage(&metabuf);
|
||||
MarkBufferDirty(metabuf.buf);
|
||||
|
||||
XLogRecGetBlockTag(record, 1, NULL, &forknum, NULL);
|
||||
if (forknum == INIT_FORKNUM)
|
||||
FlushOneBuffer(metabuf.buf);
|
||||
}
|
||||
if (BufferIsValid(metabuf.buf))
|
||||
UnlockReleaseBuffer(metabuf.buf);
|
||||
}
|
||||
|
||||
/*
|
||||
* replay a hash index insert without split
|
||||
*/
|
||||
static void hash_xlog_insert(XLogReaderState *record)
|
||||
{
|
||||
RedoBufferInfo buffer;
|
||||
RedoBufferInfo metabuf;
|
||||
|
||||
if (XLogReadBufferForRedo(record, 0, &buffer) == BLK_NEEDS_REDO) {
|
||||
Size datalen;
|
||||
char *datapos = XLogRecGetBlockData(record, 0, &datalen);
|
||||
|
||||
HashRedoInsertOperatorPage(&buffer, XLogRecGetData(record), datapos, datalen);
|
||||
MarkBufferDirty(buffer.buf);
|
||||
}
|
||||
if (BufferIsValid(buffer.buf))
|
||||
UnlockReleaseBuffer(buffer.buf);
|
||||
|
||||
if (XLogReadBufferForRedo(record, 1, &metabuf) == BLK_NEEDS_REDO) {
|
||||
/*
|
||||
* Note: in normal operation, we'd update the metapage while still
|
||||
* holding lock on the page we inserted into. But during replay it's
|
||||
* not necessary to hold that lock, since no other index updates can
|
||||
* be happening concurrently.
|
||||
*/
|
||||
HashRedoInsertOperatorMetaPage(&metabuf);
|
||||
MarkBufferDirty(metabuf.buf);
|
||||
}
|
||||
if (BufferIsValid(metabuf.buf))
|
||||
UnlockReleaseBuffer(metabuf.buf);
|
||||
}
|
||||
|
||||
/*
|
||||
* replay addition of overflow page for hash index
|
||||
*/
|
||||
static void hash_xlog_add_ovfl_page(XLogReaderState* record)
|
||||
{
|
||||
RedoBufferInfo leftbuf;
|
||||
RedoBufferInfo ovflbuf;
|
||||
RedoBufferInfo metabuf;
|
||||
BlockNumber leftblk;
|
||||
BlockNumber rightblk;
|
||||
char *data;
|
||||
Size datalen;
|
||||
|
||||
XLogRecGetBlockTag(record, 0, NULL, NULL, &rightblk);
|
||||
XLogRecGetBlockTag(record, 1, NULL, NULL, &leftblk);
|
||||
|
||||
XLogInitBufferForRedo(record, 0, &ovflbuf);
|
||||
Assert(BufferIsValid(ovflbuf.buf));
|
||||
|
||||
data = XLogRecGetBlockData(record, 0, &datalen);
|
||||
HashRedoAddOvflPageOperatorOvflPage(&ovflbuf, leftblk, data, datalen);
|
||||
MarkBufferDirty(ovflbuf.buf);
|
||||
|
||||
if (XLogReadBufferForRedo(record, 1, &leftbuf) == BLK_NEEDS_REDO) {
|
||||
HashRedoAddOvflPageOperatorLeftPage(&leftbuf, rightblk);
|
||||
MarkBufferDirty(leftbuf.buf);
|
||||
}
|
||||
|
||||
if (BufferIsValid(leftbuf.buf))
|
||||
UnlockReleaseBuffer(leftbuf.buf);
|
||||
UnlockReleaseBuffer(ovflbuf.buf);
|
||||
|
||||
/*
|
||||
* Note: in normal operation, we'd update the bitmap and meta page while
|
||||
* still holding lock on the overflow pages. But during replay it's not
|
||||
* necessary to hold those locks, since no other index updates can be
|
||||
* happening concurrently.
|
||||
*/
|
||||
if (XLogRecHasBlockRef(record, 2)) {
|
||||
RedoBufferInfo mapbuffer;
|
||||
|
||||
if (XLogReadBufferForRedo(record, 2, &mapbuffer) == BLK_NEEDS_REDO) {
|
||||
data = XLogRecGetBlockData(record, 2, &datalen);
|
||||
|
||||
HashRedoAddOvflPageOperatorMapPage(&mapbuffer, data);
|
||||
MarkBufferDirty(mapbuffer.buf);
|
||||
}
|
||||
if (BufferIsValid(mapbuffer.buf))
|
||||
UnlockReleaseBuffer(mapbuffer.buf);
|
||||
}
|
||||
|
||||
if (XLogRecHasBlockRef(record, 3)) {
|
||||
RedoBufferInfo newmapbuf;
|
||||
|
||||
XLogInitBufferForRedo(record, 3, &newmapbuf);
|
||||
|
||||
HashRedoAddOvflPageOperatorNewmapPage(&newmapbuf, XLogRecGetData(record));
|
||||
MarkBufferDirty(newmapbuf.buf);
|
||||
|
||||
UnlockReleaseBuffer(newmapbuf.buf);
|
||||
}
|
||||
|
||||
if (XLogReadBufferForRedo(record, 4, &metabuf) == BLK_NEEDS_REDO) {
|
||||
data = XLogRecGetBlockData(record, 4, &datalen);
|
||||
|
||||
HashRedoAddOvflPageOperatorMetaPage(&metabuf, XLogRecGetData(record), data, datalen);
|
||||
MarkBufferDirty(metabuf.buf);
|
||||
}
|
||||
if (BufferIsValid(metabuf.buf))
|
||||
UnlockReleaseBuffer(metabuf.buf);
|
||||
}
|
||||
|
||||
/*
|
||||
* replay allocation of page for split operation
|
||||
*/
|
||||
static void hash_xlog_split_allocate_page(XLogReaderState *record)
|
||||
{
|
||||
RedoBufferInfo oldbuf;
|
||||
RedoBufferInfo newbuf;
|
||||
RedoBufferInfo metabuf;
|
||||
Size datalen PG_USED_FOR_ASSERTS_ONLY;
|
||||
char *data;
|
||||
XLogRedoAction action;
|
||||
|
||||
/*
|
||||
* To be consistent with normal operation, here we take cleanup locks on
|
||||
* both the old and new buckets even though there can't be any concurrent
|
||||
* inserts.
|
||||
*/
|
||||
|
||||
/* replay the record for old bucket */
|
||||
action = XLogReadBufferForRedoExtended(record, 0, RBM_NORMAL, true, &oldbuf);
|
||||
|
||||
/*
|
||||
* Note that we still update the page even if it was restored from a full
|
||||
* page image, because the special space is not included in the image.
|
||||
*/
|
||||
if (action == BLK_NEEDS_REDO || action == BLK_RESTORED) {
|
||||
HashRedoSplitAllocatePageOperatorObukPage(&oldbuf, XLogRecGetData(record));
|
||||
MarkBufferDirty(oldbuf.buf);
|
||||
}
|
||||
|
||||
/* replay the record for new bucket */
|
||||
XLogInitBufferForRedo(record, 1, &newbuf);
|
||||
HashRedoSplitAllocatePageOperatorNbukPage(&newbuf, XLogRecGetData(record));
|
||||
if (!IsBufferCleanupOK(newbuf.buf))
|
||||
elog(PANIC, "hash_xlog_split_allocate_page: failed to acquire cleanup lock");
|
||||
MarkBufferDirty(newbuf.buf);
|
||||
|
||||
/*
|
||||
* We can release the lock on old bucket early as well but doing here to
|
||||
* consistent with normal operation.
|
||||
*/
|
||||
if (BufferIsValid(oldbuf.buf))
|
||||
UnlockReleaseBuffer(oldbuf.buf);
|
||||
if (BufferIsValid(newbuf.buf))
|
||||
UnlockReleaseBuffer(newbuf.buf);
|
||||
|
||||
/*
|
||||
* Note: in normal operation, we'd update the meta page while still
|
||||
* holding lock on the old and new bucket pages. But during replay it's
|
||||
* not necessary to hold those locks, since no other bucket splits can be
|
||||
* happening concurrently.
|
||||
*/
|
||||
|
||||
/* replay the record for metapage changes */
|
||||
if (XLogReadBufferForRedo(record, 2, &metabuf) == BLK_NEEDS_REDO) {
|
||||
data = XLogRecGetBlockData(record, 2, &datalen);
|
||||
|
||||
HashRedoSplitAllocatePageOperatorMetaPage(&metabuf, XLogRecGetData(record), data);
|
||||
MarkBufferDirty(metabuf.buf);
|
||||
}
|
||||
|
||||
if (BufferIsValid(metabuf.buf))
|
||||
UnlockReleaseBuffer(metabuf.buf);
|
||||
}
|
||||
|
||||
/*
|
||||
* replay of split operation
|
||||
*/
|
||||
static void hash_xlog_split_page(XLogReaderState *record)
|
||||
{
|
||||
RedoBufferInfo buf;
|
||||
|
||||
if (XLogReadBufferForRedo(record, 0, &buf) != BLK_RESTORED)
|
||||
elog(ERROR, "Hash split record did not contain a full-page image");
|
||||
|
||||
UnlockReleaseBuffer(buf.buf);
|
||||
}
|
||||
|
||||
/*
|
||||
* replay completion of split operation
|
||||
*/
|
||||
static void hash_xlog_split_complete(XLogReaderState *record)
|
||||
{
|
||||
RedoBufferInfo oldbuf;
|
||||
RedoBufferInfo newbuf;
|
||||
XLogRedoAction action;
|
||||
|
||||
/* replay the record for old bucket */
|
||||
action = XLogReadBufferForRedo(record, 0, &oldbuf);
|
||||
|
||||
/*
|
||||
* Note that we still update the page even if it was restored from a full
|
||||
* page image, because the bucket flag is not included in the image.
|
||||
*/
|
||||
if (action == BLK_NEEDS_REDO || action == BLK_RESTORED) {
|
||||
HashRedoSplitCompleteOperatorObukPage(&oldbuf, XLogRecGetData(record));
|
||||
MarkBufferDirty(oldbuf.buf);
|
||||
}
|
||||
if (BufferIsValid(oldbuf.buf))
|
||||
UnlockReleaseBuffer(oldbuf.buf);
|
||||
|
||||
/* replay the record for new bucket */
|
||||
action = XLogReadBufferForRedo(record, 1, &newbuf);
|
||||
|
||||
/*
|
||||
* Note that we still update the page even if it was restored from a full
|
||||
* page image, because the bucket flag is not included in the image.
|
||||
*/
|
||||
if (action == BLK_NEEDS_REDO || action == BLK_RESTORED) {
|
||||
HashRedoSplitCompleteOperatorNbukPage(&newbuf, XLogRecGetData(record));
|
||||
MarkBufferDirty(newbuf.buf);
|
||||
}
|
||||
if (BufferIsValid(newbuf.buf))
|
||||
UnlockReleaseBuffer(newbuf.buf);
|
||||
}
|
||||
|
||||
/*
|
||||
* replay move of page contents for squeeze operation of hash index
|
||||
*/
|
||||
static void hash_xlog_move_page_contents(XLogReaderState *record)
|
||||
{
|
||||
XLogRecPtr lsn = record->EndRecPtr;
|
||||
xl_hash_move_page_contents *xldata = (xl_hash_move_page_contents *) XLogRecGetData(record);
|
||||
RedoBufferInfo bucketbuf;
|
||||
RedoBufferInfo writebuf;
|
||||
RedoBufferInfo deletebuf;
|
||||
XLogRedoAction action;
|
||||
|
||||
bucketbuf.buf = InvalidBuffer;
|
||||
writebuf.buf = InvalidBuffer;
|
||||
deletebuf.buf = InvalidBuffer;
|
||||
|
||||
/*
|
||||
* Ensure we have a cleanup lock on primary bucket page before we start
|
||||
* with the actual replay operation. This is to ensure that neither a
|
||||
* scan can start nor a scan can be already-in-progress during the replay
|
||||
* of this operation. If we allow scans during this operation, then they
|
||||
* can miss some records or show the same record multiple times.
|
||||
*/
|
||||
if (xldata->is_prim_bucket_same_wrt)
|
||||
action = XLogReadBufferForRedoExtended(record, 1, RBM_NORMAL, true, &writebuf);
|
||||
else {
|
||||
/*
|
||||
* we don't care for return value as the purpose of reading bucketbuf
|
||||
* is to ensure a cleanup lock on primary bucket page.
|
||||
*/
|
||||
(void) XLogReadBufferForRedoExtended(record, 0, RBM_NORMAL, true, &bucketbuf);
|
||||
|
||||
PageSetLSN(bucketbuf.pageinfo.page, lsn);
|
||||
|
||||
action = XLogReadBufferForRedo(record, 1, &writebuf);
|
||||
}
|
||||
|
||||
/* replay the record for adding entries in overflow buffer */
|
||||
if (action == BLK_NEEDS_REDO) {
|
||||
Page writepage;
|
||||
char *begin;
|
||||
char *data;
|
||||
Size datalen;
|
||||
uint16 ninserted = 0;
|
||||
|
||||
data = XLogRecGetBlockData(record, 1, &datalen);
|
||||
|
||||
HashXlogMoveAddPageOperatorPage(&writebuf, XLogRecGetData(record), (void *)data, datalen);
|
||||
|
||||
MarkBufferDirty(writebuf.buf);
|
||||
}
|
||||
|
||||
/* replay the record for deleting entries from overflow buffer */
|
||||
if (XLogReadBufferForRedo(record, 2, &deletebuf) == BLK_NEEDS_REDO) {
|
||||
Page page;
|
||||
char *ptr;
|
||||
Size len;
|
||||
|
||||
ptr = XLogRecGetBlockData(record, 2, &len);
|
||||
|
||||
HashXlogMoveDeleteOvflPageOperatorPage(&deletebuf, (void *)ptr, len);
|
||||
|
||||
MarkBufferDirty(deletebuf.buf);
|
||||
}
|
||||
|
||||
/*
|
||||
* Replay is complete, now we can release the buffers. We release locks at
|
||||
* end of replay operation to ensure that we hold lock on primary bucket
|
||||
* page till end of operation. We can optimize by releasing the lock on
|
||||
* write buffer as soon as the operation for same is complete, if it is
|
||||
* not same as primary bucket page, but that doesn't seem to be worth
|
||||
* complicating the code.
|
||||
*/
|
||||
if (BufferIsValid(deletebuf.buf))
|
||||
UnlockReleaseBuffer(deletebuf.buf);
|
||||
|
||||
if (BufferIsValid(writebuf.buf))
|
||||
UnlockReleaseBuffer(writebuf.buf);
|
||||
|
||||
if (BufferIsValid(bucketbuf.buf))
|
||||
UnlockReleaseBuffer(bucketbuf.buf);
|
||||
}
|
||||
|
||||
/*
|
||||
* replay squeeze page operation of hash index
|
||||
*/
|
||||
static void hash_xlog_squeeze_page(XLogReaderState *record)
|
||||
{
|
||||
XLogRecPtr lsn = record->EndRecPtr;
|
||||
xl_hash_squeeze_page *xldata = (xl_hash_squeeze_page *) XLogRecGetData(record);
|
||||
RedoBufferInfo bucketbuf;
|
||||
RedoBufferInfo writebuf;
|
||||
RedoBufferInfo ovflbuf;
|
||||
RedoBufferInfo prevbuf;
|
||||
RedoBufferInfo mapbuf;
|
||||
XLogRedoAction action;
|
||||
|
||||
bucketbuf.buf = InvalidBuffer;
|
||||
prevbuf.buf = InvalidBuffer;
|
||||
|
||||
/*
|
||||
* Ensure we have a cleanup lock on primary bucket page before we start
|
||||
* with the actual replay operation. This is to ensure that neither a
|
||||
* scan can start nor a scan can be already-in-progress during the replay
|
||||
* of this operation. If we allow scans during this operation, then they
|
||||
* can miss some records or show the same record multiple times.
|
||||
*/
|
||||
if (xldata->is_prim_bucket_same_wrt)
|
||||
action = XLogReadBufferForRedoExtended(record, 1, RBM_NORMAL, true, &writebuf);
|
||||
else {
|
||||
/*
|
||||
* we don't care for return value as the purpose of reading bucketbuf
|
||||
* is to ensure a cleanup lock on primary bucket page.
|
||||
*/
|
||||
(void) XLogReadBufferForRedoExtended(record, 0, RBM_NORMAL, true, &bucketbuf);
|
||||
|
||||
PageSetLSN(bucketbuf.pageinfo.page, lsn);
|
||||
|
||||
action = XLogReadBufferForRedo(record, 1, &writebuf);
|
||||
}
|
||||
|
||||
/* replay the record for adding entries in overflow buffer */
|
||||
if (action == BLK_NEEDS_REDO) {
|
||||
char *data;
|
||||
Size datalen;
|
||||
|
||||
data = XLogRecGetBlockData(record, 1, &datalen);
|
||||
|
||||
HashXlogSqueezeAddPageOperatorPage(&writebuf, XLogRecGetData(record), (void *)data, datalen);
|
||||
|
||||
MarkBufferDirty(writebuf.buf);
|
||||
}
|
||||
|
||||
/* replay the record for initializing overflow buffer */
|
||||
if (XLogReadBufferForRedo(record, 2, &ovflbuf) == BLK_NEEDS_REDO) {
|
||||
HashXlogSqueezeInitOvflbufOperatorPage(&ovflbuf, XLogRecGetData(record));
|
||||
|
||||
MarkBufferDirty(ovflbuf.buf);
|
||||
}
|
||||
if (BufferIsValid(ovflbuf.buf))
|
||||
UnlockReleaseBuffer(ovflbuf.buf);
|
||||
|
||||
/* replay the record for page previous to the freed overflow page */
|
||||
if (!xldata->is_prev_bucket_same_wrt &&
|
||||
XLogReadBufferForRedo(record, 3, &prevbuf) == BLK_NEEDS_REDO) {
|
||||
HashXlogSqueezeUpdatePrevPageOperatorPage(&prevbuf, XLogRecGetData(record));
|
||||
|
||||
MarkBufferDirty(prevbuf.buf);
|
||||
}
|
||||
if (BufferIsValid(prevbuf.buf))
|
||||
UnlockReleaseBuffer(prevbuf.buf);
|
||||
|
||||
/* replay the record for page next to the freed overflow page */
|
||||
if (XLogRecHasBlockRef(record, 4)) {
|
||||
RedoBufferInfo nextbuf;
|
||||
|
||||
if (XLogReadBufferForRedo(record, 4, &nextbuf) == BLK_NEEDS_REDO) {
|
||||
HashXlogSqueezeUpdateNextPageOperatorPage(&nextbuf, XLogRecGetData(record));
|
||||
|
||||
MarkBufferDirty(nextbuf.buf);
|
||||
}
|
||||
if (BufferIsValid(nextbuf.buf))
|
||||
UnlockReleaseBuffer(nextbuf.buf);
|
||||
}
|
||||
|
||||
if (BufferIsValid(writebuf.buf))
|
||||
UnlockReleaseBuffer(writebuf.buf);
|
||||
|
||||
if (BufferIsValid(bucketbuf.buf))
|
||||
UnlockReleaseBuffer(bucketbuf.buf);
|
||||
|
||||
/*
|
||||
* Note: in normal operation, we'd update the bitmap and meta page while
|
||||
* still holding lock on the primary bucket page and overflow pages. But
|
||||
* during replay it's not necessary to hold those locks, since no other
|
||||
* index updates can be happening concurrently.
|
||||
*/
|
||||
/* replay the record for bitmap page */
|
||||
if (XLogReadBufferForRedo(record, 5, &mapbuf) == BLK_NEEDS_REDO) {
|
||||
char *data;
|
||||
Size datalen;
|
||||
|
||||
data = XLogRecGetBlockData(record, 5, &datalen);
|
||||
HashXlogSqueezeUpdateBitmapOperatorPage(&mapbuf, (void *)data);
|
||||
|
||||
MarkBufferDirty(mapbuf.buf);
|
||||
}
|
||||
if (BufferIsValid(mapbuf.buf))
|
||||
UnlockReleaseBuffer(mapbuf.buf);
|
||||
|
||||
/* replay the record for meta page */
|
||||
if (XLogRecHasBlockRef(record, 6)) {
|
||||
RedoBufferInfo metabuf;
|
||||
|
||||
if (XLogReadBufferForRedo(record, 6, &metabuf) == BLK_NEEDS_REDO) {
|
||||
char *data;
|
||||
Size datalen;
|
||||
|
||||
data = XLogRecGetBlockData(record, 6, &datalen);
|
||||
HashXlogSqueezeUpdateMateOperatorPage(&metabuf, (void *)data);
|
||||
|
||||
MarkBufferDirty(metabuf.buf);
|
||||
}
|
||||
if (BufferIsValid(metabuf.buf))
|
||||
UnlockReleaseBuffer(metabuf.buf);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* replay delete operation of hash index
|
||||
*/
|
||||
static void hash_xlog_delete(XLogReaderState *record)
|
||||
{
|
||||
XLogRecPtr lsn = record->EndRecPtr;
|
||||
xl_hash_delete *xldata = (xl_hash_delete *) XLogRecGetData(record);
|
||||
RedoBufferInfo bucketbuf;
|
||||
RedoBufferInfo deletebuf;
|
||||
XLogRedoAction action;
|
||||
|
||||
bucketbuf.buf = InvalidBuffer;
|
||||
|
||||
/*
|
||||
* Ensure we have a cleanup lock on primary bucket page before we start
|
||||
* with the actual replay operation. This is to ensure that neither a
|
||||
* scan can start nor a scan can be already-in-progress during the replay
|
||||
* of this operation. If we allow scans during this operation, then they
|
||||
* can miss some records or show the same record multiple times.
|
||||
*/
|
||||
if (xldata->is_primary_bucket_page)
|
||||
action = XLogReadBufferForRedoExtended(record, 1, RBM_NORMAL, true, &deletebuf);
|
||||
else {
|
||||
/*
|
||||
* we don't care for return value as the purpose of reading bucketbuf
|
||||
* is to ensure a cleanup lock on primary bucket page.
|
||||
*/
|
||||
(void) XLogReadBufferForRedoExtended(record, 0, RBM_NORMAL, true, &bucketbuf);
|
||||
|
||||
PageSetLSN(bucketbuf.pageinfo.page, lsn);
|
||||
|
||||
action = XLogReadBufferForRedo(record, 1, &deletebuf);
|
||||
}
|
||||
|
||||
/* replay the record for deleting entries in bucket page */
|
||||
if (action == BLK_NEEDS_REDO) {
|
||||
char *ptr;
|
||||
Size len;
|
||||
|
||||
ptr = XLogRecGetBlockData(record, 1, &len);
|
||||
|
||||
HashXlogDeleteBlockOperatorPage(&deletebuf, XLogRecGetData(record), (void *)ptr, len);
|
||||
|
||||
MarkBufferDirty(deletebuf.buf);
|
||||
}
|
||||
if (BufferIsValid(deletebuf.buf))
|
||||
UnlockReleaseBuffer(deletebuf.buf);
|
||||
|
||||
if (BufferIsValid(bucketbuf.buf))
|
||||
UnlockReleaseBuffer(bucketbuf.buf);
|
||||
}
|
||||
|
||||
/*
|
||||
* replay split cleanup flag operation for primary bucket page.
|
||||
*/
|
||||
static void hash_xlog_split_cleanup(XLogReaderState *record)
|
||||
{
|
||||
RedoBufferInfo buffer;
|
||||
|
||||
if (XLogReadBufferForRedo(record, 0, &buffer) == BLK_NEEDS_REDO) {
|
||||
HashXlogSplitCleanupOperatorPage(&buffer);
|
||||
|
||||
MarkBufferDirty(buffer.buf);
|
||||
}
|
||||
if (BufferIsValid(buffer.buf))
|
||||
UnlockReleaseBuffer(buffer.buf);
|
||||
}
|
||||
|
||||
/*
|
||||
* replay for update meta page
|
||||
*/
|
||||
static void hash_xlog_update_meta_page(XLogReaderState *record)
|
||||
{
|
||||
RedoBufferInfo metabuf;
|
||||
|
||||
if (XLogReadBufferForRedo(record, 0, &metabuf) == BLK_NEEDS_REDO) {
|
||||
HashXlogUpdateMetaOperatorPage(&metabuf, XLogRecGetData(record));
|
||||
|
||||
MarkBufferDirty(metabuf.buf);
|
||||
}
|
||||
if (BufferIsValid(metabuf.buf))
|
||||
UnlockReleaseBuffer(metabuf.buf);
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the latestRemovedXid from the heap pages pointed at by the index
|
||||
* tuples being deleted. See also btree_xlog_delete_get_latestRemovedXid,
|
||||
* on which this function is based.
|
||||
*/
|
||||
static TransactionId hash_xlog_vacuum_get_latestRemovedXid(XLogReaderState *record)
|
||||
{
|
||||
xl_hash_vacuum_one_page *xlrec;
|
||||
OffsetNumber *unused;
|
||||
Buffer ibuffer;
|
||||
Buffer hbuffer;
|
||||
Page ipage;
|
||||
Page hpage;
|
||||
RelFileNode rnode;
|
||||
BlockNumber blkno;
|
||||
ItemId iitemid;
|
||||
ItemId hitemid;
|
||||
IndexTuple itup;
|
||||
HeapTupleHeader htuphdr;
|
||||
BlockNumber hblkno;
|
||||
OffsetNumber hoffnum;
|
||||
TransactionId latestRemovedXid = InvalidTransactionId;
|
||||
int i;
|
||||
|
||||
xlrec = (xl_hash_vacuum_one_page *) XLogRecGetData(record);
|
||||
|
||||
/*
|
||||
* If there's nothing running on the standby we don't need to derive a
|
||||
* full latestRemovedXid value, so use a fast path out of here. This
|
||||
* returns InvalidTransactionId, and so will conflict with all HS
|
||||
* transactions; but since we just worked out that that's zero people,
|
||||
* it's OK.
|
||||
*
|
||||
* XXX There is a race condition here, which is that a new backend might
|
||||
* start just after we look. If so, it cannot need to conflict, but this
|
||||
* coding will result in throwing a conflict anyway.
|
||||
*/
|
||||
if (CountDBBackends(InvalidOid) == 0)
|
||||
return latestRemovedXid;
|
||||
|
||||
/*
|
||||
* Check if WAL replay has reached a consistent database state. If not, we
|
||||
* must PANIC. See the definition of
|
||||
* btree_xlog_delete_get_latestRemovedXid for more details.
|
||||
*/
|
||||
if (!t_thrd.xlog_cxt.reachedConsistency)
|
||||
elog(PANIC, "hash_xlog_vacuum_get_latestRemovedXid: cannot operate with inconsistent data");
|
||||
|
||||
/*
|
||||
* Get index page. If the DB is consistent, this should not fail, nor
|
||||
* should any of the heap page fetches below. If one does, we return
|
||||
* InvalidTransactionId to cancel all HS transactions. That's probably
|
||||
* overkill, but it's safe, and certainly better than panicking here.
|
||||
*/
|
||||
XLogRecGetBlockTag(record, 0, &rnode, NULL, &blkno);
|
||||
ibuffer = XLogReadBufferExtended(rnode, MAIN_FORKNUM, blkno, RBM_NORMAL);
|
||||
|
||||
if (!BufferIsValid(ibuffer))
|
||||
return InvalidTransactionId;
|
||||
LockBuffer(ibuffer, HASH_READ);
|
||||
ipage = (Page) BufferGetPage(ibuffer);
|
||||
|
||||
/*
|
||||
* Loop through the deleted index items to obtain the TransactionId from
|
||||
* the heap items they point to.
|
||||
*/
|
||||
unused = (OffsetNumber *) ((char *) xlrec + SizeOfHashVacuumOnePage);
|
||||
|
||||
for (i = 0; i < xlrec->ntuples; i++) {
|
||||
/*
|
||||
* Identify the index tuple about to be deleted.
|
||||
*/
|
||||
iitemid = PageGetItemId(ipage, unused[i]);
|
||||
itup = (IndexTuple) PageGetItem(ipage, iitemid);
|
||||
|
||||
/*
|
||||
* Locate the heap page that the index tuple points at
|
||||
*/
|
||||
hblkno = ItemPointerGetBlockNumber(&(itup->t_tid));
|
||||
hbuffer = XLogReadBufferExtended(xlrec->hnode, MAIN_FORKNUM, hblkno, RBM_NORMAL);
|
||||
|
||||
if (!BufferIsValid(hbuffer)) {
|
||||
UnlockReleaseBuffer(ibuffer);
|
||||
return InvalidTransactionId;
|
||||
}
|
||||
LockBuffer(hbuffer, HASH_READ);
|
||||
hpage = (Page) BufferGetPage(hbuffer);
|
||||
|
||||
/*
|
||||
* Look up the heap tuple header that the index tuple points at by
|
||||
* using the heap node supplied with the xlrec. We can't use
|
||||
* heap_fetch, since it uses ReadBuffer rather than XLogReadBuffer.
|
||||
* Note that we are not looking at tuple data here, just headers.
|
||||
*/
|
||||
hoffnum = ItemPointerGetOffsetNumber(&(itup->t_tid));
|
||||
hitemid = PageGetItemId(hpage, hoffnum);
|
||||
|
||||
/*
|
||||
* Follow any redirections until we find something useful.
|
||||
*/
|
||||
while (ItemIdIsRedirected(hitemid)) {
|
||||
hoffnum = ItemIdGetRedirect(hitemid);
|
||||
hitemid = PageGetItemId(hpage, hoffnum);
|
||||
CHECK_FOR_INTERRUPTS();
|
||||
}
|
||||
|
||||
/*
|
||||
* If the heap item has storage, then read the header and use that to
|
||||
* set latestRemovedXid.
|
||||
*
|
||||
* Some LP_DEAD items may not be accessible, so we ignore them.
|
||||
*/
|
||||
if (ItemIdHasStorage(hitemid)) {
|
||||
HeapTupleData tuple;
|
||||
tuple.t_data = (HeapTupleHeader) PageGetItem(hpage, hitemid);
|
||||
HeapTupleCopyBaseFromPage(&tuple, &hpage);
|
||||
HeapTupleHeaderAdvanceLatestRemovedXid(&tuple, &latestRemovedXid);
|
||||
} else if (ItemIdIsDead(hitemid)) {
|
||||
/*
|
||||
* Conjecture: if hitemid is dead then it had xids before the xids
|
||||
* marked on LP_NORMAL items. So we just ignore this item and move
|
||||
* onto the next, for the purposes of calculating
|
||||
* latestRemovedxids.
|
||||
*/
|
||||
} else
|
||||
Assert(!ItemIdIsUsed(hitemid));
|
||||
|
||||
UnlockReleaseBuffer(hbuffer);
|
||||
}
|
||||
|
||||
UnlockReleaseBuffer(ibuffer);
|
||||
|
||||
/*
|
||||
* If all heap tuples were LP_DEAD then we will be returning
|
||||
* InvalidTransactionId here, which avoids conflicts. This matches
|
||||
* existing logic which assumes that LP_DEAD tuples must already be older
|
||||
* than the latestRemovedXid on the cleanup record that set them as
|
||||
* LP_DEAD, hence must already have generated a conflict.
|
||||
*/
|
||||
return latestRemovedXid;
|
||||
}
|
||||
|
||||
/*
|
||||
* replay delete operation in hash index to remove
|
||||
* tuples marked as DEAD during index tuple insertion.
|
||||
*/
|
||||
static void hash_xlog_vacuum_one_page(XLogReaderState *record)
|
||||
{
|
||||
RedoBufferInfo buffer;
|
||||
RedoBufferInfo metabuf;
|
||||
XLogRedoAction action;
|
||||
|
||||
/*
|
||||
* If we have any conflict processing to do, it must happen before we
|
||||
* update the page.
|
||||
*
|
||||
* Hash index records that are marked as LP_DEAD and being removed during
|
||||
* hash index tuple insertion can conflict with standby queries. You might
|
||||
* think that vacuum records would conflict as well, but we've handled
|
||||
* that already. XLOG_HEAP2_CLEANUP_INFO records provide the highest xid
|
||||
* cleaned by the vacuum of the heap and so we can resolve any conflicts
|
||||
* just once when that arrives. After that we know that no conflicts
|
||||
* exist from individual hash index vacuum records on that index.
|
||||
*/
|
||||
if (InHotStandby) {
|
||||
TransactionId latestRemovedXid = hash_xlog_vacuum_get_latestRemovedXid(record);
|
||||
RelFileNode rnode;
|
||||
|
||||
XLogRecGetBlockTag(record, 0, &rnode, NULL, NULL);
|
||||
ResolveRecoveryConflictWithSnapshot(latestRemovedXid, rnode);
|
||||
}
|
||||
|
||||
action = XLogReadBufferForRedoExtended(record, 0, RBM_NORMAL, true, &buffer);
|
||||
|
||||
if (action == BLK_NEEDS_REDO) {
|
||||
Size len;
|
||||
|
||||
len = XLogRecGetDataLen(record);
|
||||
HashXlogVacuumOnePageOperatorPage(&buffer, XLogRecGetData(record), len);
|
||||
|
||||
MarkBufferDirty(buffer.buf);
|
||||
}
|
||||
if (BufferIsValid(buffer.buf))
|
||||
UnlockReleaseBuffer(buffer.buf);
|
||||
|
||||
if (XLogReadBufferForRedo(record, 1, &metabuf) == BLK_NEEDS_REDO) {
|
||||
HashXlogVacuumMateOperatorPage(&metabuf, XLogRecGetData(record));
|
||||
MarkBufferDirty(metabuf.buf);
|
||||
}
|
||||
if (BufferIsValid(metabuf.buf))
|
||||
UnlockReleaseBuffer(metabuf.buf);
|
||||
}
|
||||
|
||||
void hash_redo(XLogReaderState *record)
|
||||
{
|
||||
uint8 info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
|
||||
|
||||
switch (info) {
|
||||
case XLOG_HASH_INIT_META_PAGE:
|
||||
hash_xlog_init_meta_page(record);
|
||||
break;
|
||||
case XLOG_HASH_INIT_BITMAP_PAGE:
|
||||
hash_xlog_init_bitmap_page(record);
|
||||
break;
|
||||
case XLOG_HASH_INSERT:
|
||||
hash_xlog_insert(record);
|
||||
break;
|
||||
case XLOG_HASH_ADD_OVFL_PAGE:
|
||||
hash_xlog_add_ovfl_page(record);
|
||||
break;
|
||||
case XLOG_HASH_SPLIT_ALLOCATE_PAGE:
|
||||
hash_xlog_split_allocate_page(record);
|
||||
break;
|
||||
case XLOG_HASH_SPLIT_PAGE:
|
||||
hash_xlog_split_page(record);
|
||||
break;
|
||||
case XLOG_HASH_SPLIT_COMPLETE:
|
||||
hash_xlog_split_complete(record);
|
||||
break;
|
||||
case XLOG_HASH_MOVE_PAGE_CONTENTS:
|
||||
hash_xlog_move_page_contents(record);
|
||||
break;
|
||||
case XLOG_HASH_SQUEEZE_PAGE:
|
||||
hash_xlog_squeeze_page(record);
|
||||
break;
|
||||
case XLOG_HASH_DELETE:
|
||||
hash_xlog_delete(record);
|
||||
break;
|
||||
case XLOG_HASH_SPLIT_CLEANUP:
|
||||
hash_xlog_split_cleanup(record);
|
||||
break;
|
||||
case XLOG_HASH_UPDATE_META_PAGE:
|
||||
hash_xlog_update_meta_page(record);
|
||||
break;
|
||||
case XLOG_HASH_VACUUM_ONE_PAGE:
|
||||
hash_xlog_vacuum_one_page(record);
|
||||
break;
|
||||
default:
|
||||
elog(PANIC, "hash_redo: unknown op code %u", info);
|
||||
}
|
||||
}
|
||||
|
||||
bool IsHashVacuumPages(XLogReaderState *record)
|
||||
{
|
||||
uint8 info = (XLogRecGetInfo(record) & (~XLR_INFO_MASK));
|
||||
|
||||
if (XLogRecGetRmid(record) == RM_HASH_ID) {
|
||||
if (info == XLOG_HASH_DELETE) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
@ -843,6 +843,9 @@ void XLogBlockDataCommonRedo(XLogBlockHead *blockhead, void *blockrecbody, RedoB
|
|||
case RM_BTREE_ID:
|
||||
BtreeRedoDataBlock(blockhead, blockdatarec, bufferinfo);
|
||||
break;
|
||||
case RM_HASH_ID:
|
||||
HashRedoDataBlock(blockhead, blockdatarec, bufferinfo);
|
||||
break;
|
||||
case RM_XLOG_ID:
|
||||
xlog_redo_data_block(blockhead, blockdatarec, bufferinfo);
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -16,9 +16,155 @@
|
|||
#include "postgres.h"
|
||||
#include "knl/knl_variable.h"
|
||||
|
||||
#include "access/hash.h"
|
||||
#include "access/rmgr.h"
|
||||
#include "access/hash_xlog.h"
|
||||
|
||||
void hash_desc(StringInfo buf, XLogReaderState *record)
|
||||
{
|
||||
/* nothing to do */
|
||||
char *rec = XLogRecGetData(record);
|
||||
uint8 info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
|
||||
|
||||
switch (info) {
|
||||
case XLOG_HASH_INIT_META_PAGE:
|
||||
{
|
||||
xl_hash_init_meta_page *xlrec = (xl_hash_init_meta_page *) rec;
|
||||
|
||||
appendStringInfo(buf, "num_tuples %g, fillfactor %d",
|
||||
xlrec->num_tuples, xlrec->ffactor);
|
||||
break;
|
||||
}
|
||||
case XLOG_HASH_INIT_BITMAP_PAGE:
|
||||
{
|
||||
xl_hash_init_bitmap_page *xlrec = (xl_hash_init_bitmap_page *) rec;
|
||||
|
||||
appendStringInfo(buf, "bmsize %d", xlrec->bmsize);
|
||||
break;
|
||||
}
|
||||
case XLOG_HASH_INSERT:
|
||||
{
|
||||
xl_hash_insert *xlrec = (xl_hash_insert *) rec;
|
||||
|
||||
appendStringInfo(buf, "off %u", xlrec->offnum);
|
||||
break;
|
||||
}
|
||||
case XLOG_HASH_ADD_OVFL_PAGE:
|
||||
{
|
||||
xl_hash_add_ovfl_page *xlrec = (xl_hash_add_ovfl_page *) rec;
|
||||
|
||||
appendStringInfo(buf, "bmsize %d, bmpage_found %c",
|
||||
xlrec->bmsize, (xlrec->bmpage_found) ? 'T' : 'F');
|
||||
break;
|
||||
}
|
||||
case XLOG_HASH_SPLIT_ALLOCATE_PAGE:
|
||||
{
|
||||
xl_hash_split_allocate_page *xlrec = (xl_hash_split_allocate_page *) rec;
|
||||
|
||||
appendStringInfo(buf, "new_bucket %u, meta_page_masks_updated %c, issplitpoint_changed %c",
|
||||
xlrec->new_bucket,
|
||||
(xlrec->flags & XLH_SPLIT_META_UPDATE_MASKS) ? 'T' : 'F',
|
||||
(xlrec->flags & XLH_SPLIT_META_UPDATE_SPLITPOINT) ? 'T' : 'F');
|
||||
break;
|
||||
}
|
||||
case XLOG_HASH_SPLIT_COMPLETE:
|
||||
{
|
||||
xl_hash_split_complete *xlrec = (xl_hash_split_complete *) rec;
|
||||
|
||||
appendStringInfo(buf, "old_bucket_flag %u, new_bucket_flag %u",
|
||||
xlrec->old_bucket_flag, xlrec->new_bucket_flag);
|
||||
break;
|
||||
}
|
||||
case XLOG_HASH_MOVE_PAGE_CONTENTS:
|
||||
{
|
||||
xl_hash_move_page_contents *xlrec = (xl_hash_move_page_contents *) rec;
|
||||
|
||||
appendStringInfo(buf, "ntups %d, is_primary %c",
|
||||
xlrec->ntups,
|
||||
xlrec->is_prim_bucket_same_wrt ? 'T' : 'F');
|
||||
break;
|
||||
}
|
||||
case XLOG_HASH_SQUEEZE_PAGE:
|
||||
{
|
||||
xl_hash_squeeze_page *xlrec = (xl_hash_squeeze_page *) rec;
|
||||
|
||||
appendStringInfo(buf, "prevblkno %u, nextblkno %u, ntups %d, is_primary %c",
|
||||
xlrec->prevblkno,
|
||||
xlrec->nextblkno,
|
||||
xlrec->ntups,
|
||||
xlrec->is_prim_bucket_same_wrt ? 'T' : 'F');
|
||||
break;
|
||||
}
|
||||
case XLOG_HASH_DELETE:
|
||||
{
|
||||
xl_hash_delete *xlrec = (xl_hash_delete *) rec;
|
||||
|
||||
appendStringInfo(buf, "clear_dead_marking %c, is_primary %c",
|
||||
xlrec->clear_dead_marking ? 'T' : 'F',
|
||||
xlrec->is_primary_bucket_page ? 'T' : 'F');
|
||||
break;
|
||||
}
|
||||
case XLOG_HASH_UPDATE_META_PAGE:
|
||||
{
|
||||
xl_hash_update_meta_page *xlrec = (xl_hash_update_meta_page *) rec;
|
||||
|
||||
appendStringInfo(buf, "ntuples %g",
|
||||
xlrec->ntuples);
|
||||
break;
|
||||
}
|
||||
case XLOG_HASH_VACUUM_ONE_PAGE:
|
||||
{
|
||||
xl_hash_vacuum_one_page *xlrec = (xl_hash_vacuum_one_page *) rec;
|
||||
|
||||
appendStringInfo(buf, "ntuples %d",
|
||||
xlrec->ntuples);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const char *hash_identify(uint8 info)
|
||||
{
|
||||
const char *id = NULL;
|
||||
|
||||
switch (info & ~XLR_INFO_MASK) {
|
||||
case XLOG_HASH_INIT_META_PAGE:
|
||||
id = "INIT_META_PAGE";
|
||||
break;
|
||||
case XLOG_HASH_INIT_BITMAP_PAGE:
|
||||
id = "INIT_BITMAP_PAGE";
|
||||
break;
|
||||
case XLOG_HASH_INSERT:
|
||||
id = "INSERT";
|
||||
break;
|
||||
case XLOG_HASH_ADD_OVFL_PAGE:
|
||||
id = "ADD_OVFL_PAGE";
|
||||
break;
|
||||
case XLOG_HASH_SPLIT_ALLOCATE_PAGE:
|
||||
id = "SPLIT_ALLOCATE_PAGE";
|
||||
break;
|
||||
case XLOG_HASH_SPLIT_PAGE:
|
||||
id = "SPLIT_PAGE";
|
||||
break;
|
||||
case XLOG_HASH_SPLIT_COMPLETE:
|
||||
id = "SPLIT_COMPLETE";
|
||||
break;
|
||||
case XLOG_HASH_MOVE_PAGE_CONTENTS:
|
||||
id = "MOVE_PAGE_CONTENTS";
|
||||
break;
|
||||
case XLOG_HASH_SQUEEZE_PAGE:
|
||||
id = "SQUEEZE_PAGE";
|
||||
break;
|
||||
case XLOG_HASH_DELETE:
|
||||
id = "DELETE";
|
||||
break;
|
||||
case XLOG_HASH_SPLIT_CLEANUP:
|
||||
id = "SPLIT_CLEANUP";
|
||||
break;
|
||||
case XLOG_HASH_UPDATE_META_PAGE:
|
||||
id = "UPDATE_META_PAGE";
|
||||
break;
|
||||
case XLOG_HASH_VACUUM_ONE_PAGE:
|
||||
id = "VACUUM_ONE_PAGE";
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
|
|
@ -31,6 +31,7 @@
|
|||
#include "access/xact.h"
|
||||
#include "access/xlog_internal.h"
|
||||
#include "access/nbtree.h"
|
||||
#include "access/hash_xlog.h"
|
||||
#include "access/xlogreader.h"
|
||||
#include "access/gist_private.h"
|
||||
#include "access/multixact.h"
|
||||
|
|
@ -165,7 +166,7 @@ static const RmgrDispatchData g_dispatchTable[RM_MAX_ID + 1] = {
|
|||
{ DispatchHeap2Record, RmgrRecordInfoValid, RM_HEAP2_ID, XLOG_HEAP2_FREEZE, XLOG_HEAP2_LOGICAL_NEWPAGE },
|
||||
{ DispatchHeapRecord, RmgrRecordInfoValid, RM_HEAP_ID, XLOG_HEAP_INSERT, XLOG_HEAP_INPLACE },
|
||||
{ DispatchBtreeRecord, RmgrRecordInfoValid, RM_BTREE_ID, XLOG_BTREE_INSERT_LEAF, XLOG_BTREE_REUSE_PAGE },
|
||||
{ DispatchHashRecord, NULL, RM_HASH_ID, 0, 0 },
|
||||
{ DispatchHashRecord, RmgrRecordInfoValid, RM_HASH_ID, XLOG_HASH_INIT_META_PAGE, XLOG_HASH_VACUUM_ONE_PAGE },
|
||||
{ DispatchGinRecord, RmgrRecordInfoValid, RM_GIN_ID, XLOG_GIN_CREATE_INDEX, XLOG_GIN_VACUUM_DATA_LEAF_PAGE },
|
||||
/* XLOG_GIST_PAGE_DELETE is not used and info isn't continus */
|
||||
{ DispatchGistRecord, RmgrGistRecordInfoValid, RM_GIST_ID, 0, 0 },
|
||||
|
|
@ -912,8 +913,20 @@ static bool DispatchCLogRecord(XLogReaderState *record, List *expectedTLIs, Time
|
|||
/* Run from the dispatcher thread. */
|
||||
static bool DispatchHashRecord(XLogReaderState *record, List *expectedTLIs, TimestampTz recordXTime)
|
||||
{
|
||||
DispatchTxnRecord(record, expectedTLIs, recordXTime, false);
|
||||
return true;
|
||||
bool isNeedFullSync = false;
|
||||
|
||||
/* index not support mvcc, so we need to sync with trx thread when the record is vacuum */
|
||||
if (IsHashVacuumPages(record) && g_supportHotStandby) {
|
||||
GetWorkerIds(record, ANY_WORKER, true);
|
||||
/* sync with trxn thread */
|
||||
/* only need to process in pageworker thread, wait trxn sync */
|
||||
/* pageworker exe, trxn don't need exe */
|
||||
DispatchToSpecPageWorker(record, expectedTLIs, true);
|
||||
} else {
|
||||
DispatchRecordWithPages(record, expectedTLIs, true);
|
||||
}
|
||||
|
||||
return isNeedFullSync;
|
||||
}
|
||||
|
||||
static bool DispatchBtreeRecord(XLogReaderState *record, List *expectedTLIs, TimestampTz recordXTime)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,352 @@
|
|||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* hash_xlog.h
|
||||
* header file for Postgres hash AM implementation
|
||||
*
|
||||
* Portions Copyright (c) 2021 Huawei Technologies Co.,Ltd.
|
||||
* Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* src/include/access/hash_xlog.h
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#ifndef HASH_XLOG_H
|
||||
#define HASH_XLOG_H
|
||||
|
||||
#include "access/xlogreader.h"
|
||||
#include "lib/stringinfo.h"
|
||||
#include "storage/off.h"
|
||||
|
||||
/* Number of buffers required for XLOG_HASH_SQUEEZE_PAGE operation */
|
||||
#define HASH_XLOG_FREE_OVFL_BUFS 6
|
||||
|
||||
/*
|
||||
* XLOG records for hash operations
|
||||
*/
|
||||
#define XLOG_HASH_INIT_META_PAGE 0x00 /* initialize the meta page */
|
||||
#define XLOG_HASH_INIT_BITMAP_PAGE 0x10 /* initialize the bitmap page */
|
||||
#define XLOG_HASH_INSERT 0x20 /* add index tuple without split */
|
||||
#define XLOG_HASH_ADD_OVFL_PAGE 0x30 /* add overflow page */
|
||||
#define XLOG_HASH_SPLIT_ALLOCATE_PAGE 0x40 /* allocate new page for split */
|
||||
#define XLOG_HASH_SPLIT_PAGE 0x50 /* split page */
|
||||
#define XLOG_HASH_SPLIT_COMPLETE 0x60 /* completion of split operation */
|
||||
#define XLOG_HASH_MOVE_PAGE_CONTENTS 0x70 /* remove tuples from one page
|
||||
* and add to another page */
|
||||
#define XLOG_HASH_SQUEEZE_PAGE 0x80 /* add tuples to one of the previous
|
||||
* pages in chain and free the ovfl
|
||||
* page */
|
||||
#define XLOG_HASH_DELETE 0x90 /* delete index tuples from a page */
|
||||
#define XLOG_HASH_SPLIT_CLEANUP 0xA0 /* clear split-cleanup flag in primary
|
||||
* bucket page after deleting tuples
|
||||
* that are moved due to split */
|
||||
#define XLOG_HASH_UPDATE_META_PAGE 0xB0 /* update meta page after vacuum */
|
||||
#define XLOG_HASH_VACUUM_ONE_PAGE 0xC0 /* remove dead tuples from index page */
|
||||
|
||||
enum {
|
||||
XLOG_HASH_INIT_META_PAGE_NUM = 0,
|
||||
};
|
||||
|
||||
enum {
|
||||
XLOG_HASH_INIT_BITMAP_PAGE_BITMAP_NUM = 0,
|
||||
XLOG_HASH_INIT_BITMAP_PAGE_META_NUM,
|
||||
};
|
||||
|
||||
enum {
|
||||
XLOG_HASH_INSERT_PAGE_NUM = 0,
|
||||
XLOG_HASH_INSERT_META_NUM,
|
||||
};
|
||||
|
||||
enum {
|
||||
XLOG_HASH_ADD_OVFL_PAGE_OVFL_NUM = 0,
|
||||
XLOG_HASH_ADD_OVFL_PAGE_LEFT_NUM,
|
||||
XLOG_HASH_ADD_OVFL_PAGE_MAP_NUM,
|
||||
XLOG_HASH_ADD_OVFL_PAGE_NEWMAP_NUM,
|
||||
XLOG_HASH_ADD_OVFL_PAGE_META_NUM,
|
||||
};
|
||||
|
||||
enum {
|
||||
XLOG_HASH_SPLIT_ALLOCATE_PAGE_OBUK_NUM = 0,
|
||||
XLOG_HASH_SPLIT_ALLOCATE_PAGE_NBUK_NUM,
|
||||
XLOG_HASH_SPLIT_ALLOCATE_PAGE_META_NUM,
|
||||
};
|
||||
|
||||
enum {
|
||||
XLOG_HASH_SPLIT_PAGE_NUM = 0,
|
||||
};
|
||||
|
||||
enum {
|
||||
XLOG_HASH_SPLIT_COMPLETE_OBUK_NUM = 0,
|
||||
XLOG_HASH_SPLIT_COMPLETE_NBUK_NUM,
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
HASH_MOVE_BUK_BLOCK_NUM = 0,
|
||||
HASH_MOVE_ADD_BLOCK_NUM,
|
||||
HASH_MOVE_DELETE_OVFL_BLOCK_NUM,
|
||||
}XLogHashMovePageEnum;
|
||||
|
||||
typedef enum {
|
||||
HASH_SQUEEZE_BUK_BLOCK_NUM = 0,
|
||||
HASH_SQUEEZE_ADD_BLOCK_NUM,
|
||||
HASH_SQUEEZE_INIT_OVFLBUF_BLOCK_NUM,
|
||||
HASH_SQUEEZE_UPDATE_PREV_BLOCK_NUM,
|
||||
HASH_SQUEEZE_UPDATE_NEXT_BLOCK_NUM,
|
||||
HASH_SQUEEZE_UPDATE_BITMAP_BLOCK_NUM,
|
||||
HASH_SQUEEZE_UPDATE_MATE_BLOCK_NUM,
|
||||
}XLogHashSqueezePageEnum;
|
||||
|
||||
typedef enum {
|
||||
HASH_DELETE_BUK_BLOCK_NUM = 0,
|
||||
HASH_DELETE_OVFL_BLOCK_NUM,
|
||||
}XLogHashDeleteEnum;
|
||||
|
||||
typedef enum {
|
||||
HASH_SPLIT_CLEANUP_BLOCK_NUM,
|
||||
}XLogHashSplitCleanupEnum;
|
||||
|
||||
typedef enum {
|
||||
HASH_UPDATE_MATE_BLOCK_NUM,
|
||||
} XLogHashUpdateMateEnum;
|
||||
|
||||
typedef enum {
|
||||
HASH_VACUUM_PAGE_BLOCK_NUM = 0,
|
||||
HASH_VACUUM_META_BLOCK_NUM,
|
||||
} XLogHashVacuumPageEnum;
|
||||
|
||||
/*
|
||||
* xl_hash_split_allocate_page flag values, 8 bits are available.
|
||||
*/
|
||||
#define XLH_SPLIT_META_UPDATE_MASKS (1<<0)
|
||||
#define XLH_SPLIT_META_UPDATE_SPLITPOINT (1<<1)
|
||||
|
||||
/*
|
||||
* This is what we need to know about a HASH index create.
|
||||
*
|
||||
* Backup block 0: metapage
|
||||
*/
|
||||
typedef struct xl_hash_createidx
|
||||
{
|
||||
double num_tuples;
|
||||
RegProcedure procid;
|
||||
uint16 ffactor;
|
||||
} xl_hash_createidx;
|
||||
|
||||
#define SizeOfHashCreateIdx (offsetof(xl_hash_createidx, ffactor) + sizeof(uint16))
|
||||
|
||||
/*
|
||||
* This is what we need to know about simple (without split) insert.
|
||||
*
|
||||
* This data record is used for XLOG_HASH_INSERT
|
||||
*
|
||||
* Backup Blk 0: original page (data contains the inserted tuple)
|
||||
* Backup Blk 1: metapage (HashMetaPageData)
|
||||
*/
|
||||
typedef struct xl_hash_insert
|
||||
{
|
||||
OffsetNumber offnum;
|
||||
} xl_hash_insert;
|
||||
|
||||
#define SizeOfHashInsert (offsetof(xl_hash_insert, offnum) + sizeof(OffsetNumber))
|
||||
|
||||
/*
|
||||
* This is what we need to know about addition of overflow page.
|
||||
*
|
||||
* This data record is used for XLOG_HASH_ADD_OVFL_PAGE
|
||||
*
|
||||
* Backup Blk 0: newly allocated overflow page
|
||||
* Backup Blk 1: page before new overflow page in the bucket chain
|
||||
* Backup Blk 2: bitmap page
|
||||
* Backup Blk 3: new bitmap page
|
||||
* Backup Blk 4: metapage
|
||||
*/
|
||||
typedef struct xl_hash_add_ovfl_page
|
||||
{
|
||||
uint16 bmsize;
|
||||
bool bmpage_found;
|
||||
} xl_hash_add_ovfl_page;
|
||||
|
||||
#define SizeOfHashAddOvflPage \
|
||||
(offsetof(xl_hash_add_ovfl_page, bmpage_found) + sizeof(bool))
|
||||
|
||||
/*
|
||||
* This is what we need to know about allocating a page for split.
|
||||
*
|
||||
* This data record is used for XLOG_HASH_SPLIT_ALLOCATE_PAGE
|
||||
*
|
||||
* Backup Blk 0: page for old bucket
|
||||
* Backup Blk 1: page for new bucket
|
||||
* Backup Blk 2: metapage
|
||||
*/
|
||||
typedef struct xl_hash_split_allocate_page
|
||||
{
|
||||
uint32 new_bucket;
|
||||
uint16 old_bucket_flag;
|
||||
uint16 new_bucket_flag;
|
||||
uint8 flags;
|
||||
} xl_hash_split_allocate_page;
|
||||
|
||||
#define SizeOfHashSplitAllocPage \
|
||||
(offsetof(xl_hash_split_allocate_page, flags) + sizeof(uint8))
|
||||
|
||||
/*
|
||||
* This is what we need to know about completing the split operation.
|
||||
*
|
||||
* This data record is used for XLOG_HASH_SPLIT_COMPLETE
|
||||
*
|
||||
* Backup Blk 0: page for old bucket
|
||||
* Backup Blk 1: page for new bucket
|
||||
*/
|
||||
typedef struct xl_hash_split_complete
|
||||
{
|
||||
uint16 old_bucket_flag;
|
||||
uint16 new_bucket_flag;
|
||||
} xl_hash_split_complete;
|
||||
|
||||
#define SizeOfHashSplitComplete \
|
||||
(offsetof(xl_hash_split_complete, new_bucket_flag) + sizeof(uint16))
|
||||
|
||||
/*
|
||||
* This is what we need to know about move page contents required during
|
||||
* squeeze operation.
|
||||
*
|
||||
* This data record is used for XLOG_HASH_MOVE_PAGE_CONTENTS
|
||||
*
|
||||
* Backup Blk 0: bucket page
|
||||
* Backup Blk 1: page containing moved tuples
|
||||
* Backup Blk 2: page from which tuples will be removed
|
||||
*/
|
||||
typedef struct xl_hash_move_page_contents
|
||||
{
|
||||
uint16 ntups;
|
||||
bool is_prim_bucket_same_wrt; /* true if the page to which
|
||||
* tuples are moved is same as
|
||||
* primary bucket page */
|
||||
} xl_hash_move_page_contents;
|
||||
|
||||
#define SizeOfHashMovePageContents \
|
||||
(offsetof(xl_hash_move_page_contents, is_prim_bucket_same_wrt) + sizeof(bool))
|
||||
|
||||
/*
|
||||
* This is what we need to know about the squeeze page operation.
|
||||
*
|
||||
* This data record is used for XLOG_HASH_SQUEEZE_PAGE
|
||||
*
|
||||
* Backup Blk 0: page containing tuples moved from freed overflow page
|
||||
* Backup Blk 1: freed overflow page
|
||||
* Backup Blk 2: page previous to the freed overflow page
|
||||
* Backup Blk 3: page next to the freed overflow page
|
||||
* Backup Blk 4: bitmap page containing info of freed overflow page
|
||||
* Backup Blk 5: meta page
|
||||
*/
|
||||
typedef struct xl_hash_squeeze_page
|
||||
{
|
||||
BlockNumber prevblkno;
|
||||
BlockNumber nextblkno;
|
||||
uint16 ntups;
|
||||
bool is_prim_bucket_same_wrt; /* true if the page to which
|
||||
* tuples are moved is same as
|
||||
* primary bucket page */
|
||||
bool is_prev_bucket_same_wrt; /* true if the page to which
|
||||
* tuples are moved is the page
|
||||
* previous to the freed overflow
|
||||
* page */
|
||||
} xl_hash_squeeze_page;
|
||||
|
||||
#define SizeOfHashSqueezePage \
|
||||
(offsetof(xl_hash_squeeze_page, is_prev_bucket_same_wrt) + sizeof(bool))
|
||||
|
||||
/*
|
||||
* This is what we need to know about the deletion of index tuples from a page.
|
||||
*
|
||||
* This data record is used for XLOG_HASH_DELETE
|
||||
*
|
||||
* Backup Blk 0: primary bucket page
|
||||
* Backup Blk 1: page from which tuples are deleted
|
||||
*/
|
||||
typedef struct xl_hash_delete
|
||||
{
|
||||
bool clear_dead_marking; /* true if this operation clears
|
||||
* LH_PAGE_HAS_DEAD_TUPLES flag */
|
||||
bool is_primary_bucket_page; /* true if the operation is for
|
||||
* primary bucket page */
|
||||
} xl_hash_delete;
|
||||
|
||||
#define SizeOfHashDelete \
|
||||
(offsetof(xl_hash_delete, is_primary_bucket_page) + sizeof(bool))
|
||||
|
||||
/*
|
||||
* This is what we need for metapage update operation.
|
||||
*
|
||||
* This data record is used for XLOG_HASH_UPDATE_META_PAGE
|
||||
*
|
||||
* Backup Blk 0: meta page
|
||||
*/
|
||||
typedef struct xl_hash_update_meta_page
|
||||
{
|
||||
double ntuples;
|
||||
} xl_hash_update_meta_page;
|
||||
|
||||
#define SizeOfHashUpdateMetaPage \
|
||||
(offsetof(xl_hash_update_meta_page, ntuples) + sizeof(double))
|
||||
|
||||
/*
|
||||
* This is what we need to initialize metapage.
|
||||
*
|
||||
* This data record is used for XLOG_HASH_INIT_META_PAGE
|
||||
*
|
||||
* Backup Blk 0: meta page
|
||||
*/
|
||||
typedef struct xl_hash_init_meta_page
|
||||
{
|
||||
double num_tuples;
|
||||
RegProcedure procid;
|
||||
uint16 ffactor;
|
||||
} xl_hash_init_meta_page;
|
||||
|
||||
#define SizeOfHashInitMetaPage \
|
||||
(offsetof(xl_hash_init_meta_page, ffactor) + sizeof(uint16))
|
||||
|
||||
/*
|
||||
* This is what we need to initialize bitmap page.
|
||||
*
|
||||
* This data record is used for XLOG_HASH_INIT_BITMAP_PAGE
|
||||
*
|
||||
* Backup Blk 0: bitmap page
|
||||
* Backup Blk 1: meta page
|
||||
*/
|
||||
typedef struct xl_hash_init_bitmap_page
|
||||
{
|
||||
uint16 bmsize;
|
||||
} xl_hash_init_bitmap_page;
|
||||
|
||||
#define SizeOfHashInitBitmapPage \
|
||||
(offsetof(xl_hash_init_bitmap_page, bmsize) + sizeof(uint16))
|
||||
|
||||
/*
|
||||
* This is what we need for index tuple deletion and to
|
||||
* update the meta page.
|
||||
*
|
||||
* This data record is used for XLOG_HASH_VACUUM_ONE_PAGE
|
||||
*
|
||||
* Backup Blk 0: bucket page
|
||||
* Backup Blk 1: meta page
|
||||
*/
|
||||
typedef struct xl_hash_vacuum_one_page
|
||||
{
|
||||
RelFileNode hnode;
|
||||
int ntuples;
|
||||
|
||||
/* TARGET OFFSET NUMBERS FOLLOW AT THE END */
|
||||
} xl_hash_vacuum_one_page;
|
||||
|
||||
#define SizeOfHashVacuumOnePage \
|
||||
(offsetof(xl_hash_vacuum_one_page, ntuples) + sizeof(int))
|
||||
|
||||
extern void hash_redo(XLogReaderState *record);
|
||||
extern void hash_desc(StringInfo buf, XLogReaderState *record);
|
||||
extern const char *hash_identify(uint8 info);
|
||||
extern bool IsHashVacuumPages(XLogReaderState *record);
|
||||
|
||||
#endif /* HASH_XLOG_H */
|
||||
|
|
@ -754,6 +754,47 @@ void BtreeXlogUnlinkPageOperatorChildpage(RedoBufferInfo* cbuf, void* recorddata
|
|||
|
||||
void BtreeXlogClearIncompleteSplit(RedoBufferInfo* buffer);
|
||||
|
||||
void HashRedoInitMetaPageOperatorPage(RedoBufferInfo *metabuf, void *recorddata);
|
||||
|
||||
void HashRedoInitBitmapPageOperatorBitmapPage(RedoBufferInfo *bitmapbuf, void *recorddata);
|
||||
void HashRedoInitBitmapPageOperatorMetaPage(RedoBufferInfo *metabuf);
|
||||
|
||||
void HashRedoInsertOperatorPage(RedoBufferInfo *buffer, void *recorddata, void *data, Size datalen);
|
||||
void HashRedoInsertOperatorMetaPage(RedoBufferInfo *metabuf);
|
||||
|
||||
void HashRedoAddOvflPageOperatorOvflPage(RedoBufferInfo *ovflbuf, BlockNumber leftblk, void *data, Size datalen);
|
||||
void HashRedoAddOvflPageOperatorLeftPage(RedoBufferInfo *ovflbuf, BlockNumber rightblk);
|
||||
void HashRedoAddOvflPageOperatorMapPage(RedoBufferInfo *mapbuf, void *data);
|
||||
void HashRedoAddOvflPageOperatorNewmapPage(RedoBufferInfo *newmapbuf, void *recorddata);
|
||||
void HashRedoAddOvflPageOperatorMetaPage(RedoBufferInfo *metabuf, void *recorddata, void *data, Size datalen);
|
||||
|
||||
void HashRedoSplitAllocatePageOperatorObukPage(RedoBufferInfo *oldbukbuf, void *recorddata);
|
||||
void HashRedoSplitAllocatePageOperatorNbukPage(RedoBufferInfo *newbukbuf, void *recorddata);
|
||||
void HashRedoSplitAllocatePageOperatorMetaPage(RedoBufferInfo *metabuf, void *recorddata, void *data);
|
||||
|
||||
void HashRedoSplitCompleteOperatorObukPage(RedoBufferInfo *oldbukbuf, void *recorddata);
|
||||
void HashRedoSplitCompleteOperatorNbukPage(RedoBufferInfo *newbukbuf, void *recorddata);
|
||||
|
||||
void HashXlogMoveAddPageOperatorPage(RedoBufferInfo *redobuffer, void *recorddata, void *blkdata, Size len);
|
||||
void HashXlogMoveDeleteOvflPageOperatorPage(RedoBufferInfo *redobuffer, void *blkdata, Size len);
|
||||
|
||||
void HashXlogSqueezeAddPageOperatorPage(RedoBufferInfo *redobuffer, void *recorddata, void *blkdata, Size len);
|
||||
void HashXlogSqueezeInitOvflbufOperatorPage(RedoBufferInfo *redobuffer, void *recorddata);
|
||||
void HashXlogSqueezeUpdatePrevPageOperatorPage(RedoBufferInfo *redobuffer, void *recorddata);
|
||||
void HashXlogSqueezeUpdateNextPageOperatorPage(RedoBufferInfo *redobuffer, void *recorddata);
|
||||
void HashXlogSqueezeUpdateBitmapOperatorPage(RedoBufferInfo *redobuffer, void *blkdata);
|
||||
void HashXlogSqueezeUpdateMateOperatorPage(RedoBufferInfo *redobuffer, void *blkdata);
|
||||
|
||||
void HashXlogDeleteBlockOperatorPage(RedoBufferInfo *redobuffer, void *recorddata, void *blkdata, Size len);
|
||||
|
||||
void HashXlogSplitCleanupOperatorPage(RedoBufferInfo *redobuffer);
|
||||
|
||||
void HashXlogUpdateMetaOperatorPage(RedoBufferInfo *redobuffer, void *recorddata);
|
||||
|
||||
void HashXlogVacuumOnePageOperatorPage(RedoBufferInfo *redobuffer, void *recorddata, Size len);
|
||||
|
||||
void HashXlogVacuumMateOperatorPage(RedoBufferInfo *redobuffer, void *recorddata);
|
||||
|
||||
void XLogRecSetBlockCommonState(XLogReaderState* record, XLogBlockParseEnum blockvalid, ForkNumber forknum,
|
||||
BlockNumber blockknum, RelFileNode* relnode, XLogRecParseState* recordblockstate);
|
||||
|
||||
|
|
@ -787,6 +828,7 @@ extern void XLogRecSetBlockDdlState(XLogBlockDdlParse* blockddlstate, uint32 blo
|
|||
char *mainData, Oid ownerid = InvalidOid);
|
||||
XLogRedoAction XLogCheckBlockDataRedoAction(XLogBlockDataParse* datadecode, RedoBufferInfo* bufferinfo);
|
||||
void BtreeRedoDataBlock(XLogBlockHead* blockhead, XLogBlockDataParse* blockdatarec, RedoBufferInfo* bufferinfo);
|
||||
extern void HashRedoDataBlock(XLogBlockHead* blockhead, XLogBlockDataParse* blockdatarec, RedoBufferInfo* bufferinfo);
|
||||
XLogRecParseState* XactXlogCsnlogParseToBlock(XLogReaderState* record, uint32* blocknum, TransactionId xid,
|
||||
int nsubxids, TransactionId* subxids, CommitSeqNo csn, XLogRecParseState* recordstatehead);
|
||||
extern void XLogRecSetVmBlockState(XLogReaderState* record, uint32 blockid, XLogRecParseState* recordblockstate);
|
||||
|
|
@ -914,5 +956,4 @@ extern void XLogBlockDdlDoSmgrAction(XLogBlockHead* blockhead, void* blockrecbod
|
|||
extern void GinRedoDataBlock(XLogBlockHead* blockhead, XLogBlockDataParse* blockdatarec, RedoBufferInfo* bufferinfo);
|
||||
extern void GistRedoDataBlock(XLogBlockHead *blockhead, XLogBlockDataParse *blockdatarec, RedoBufferInfo *bufferinfo);
|
||||
extern bool IsCheckPoint(const XLogRecParseState *parseState);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -55,6 +55,7 @@ extern const uint32 RANGE_LIST_DISTRIBUTION_VERSION_NUM;
|
|||
extern const uint32 FIX_SQL_ADD_RELATION_REF_COUNT;
|
||||
extern const uint32 GENERATED_COL_VERSION_NUM;
|
||||
extern const uint32 ANALYZER_HOOK_VERSION_NUM;
|
||||
extern const uint32 SUPPORT_HASH_XLOG_VERSION_NUM;
|
||||
|
||||
#define INPLACE_UPGRADE_PRECOMMIT_VERSION 1
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue