* Copyright (c) 2020 Huawei Technologies Co.,Ltd.
*
* openGauss is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*
* http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
* -------------------------------------------------------------------------
*
* heapam.cpp
* parse heap xlog
*
* IDENTIFICATION
* src/gausskernel/storage/access/redo/heapam.cpp
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "knl/knl_variable.h"
#include "access/heapam.h"
#include "access/hio.h"
#include "access/multixact.h"
#include "access/relscan.h"
#include "access/sysattr.h"
#include "access/transam.h"
#include "access/tuptoaster.h"
#include "access/valid.h"
#include "access/visibilitymap.h"
#include "access/xact.h"
#include "access/xlog.h"
#include "access/xloginsert.h"
#include "access/xlogutils.h"
#include "access/xlogproc.h"
#include "access/multi_redo_api.h"
#include "catalog/catalog.h"
#include "catalog/namespace.h"
#include "catalog/pg_proc.h"
#include "commands/dbcommands.h"
#include "executor/node/nodeModifyTable.h"
#include "replication/dataqueue.h"
#include "replication/datasender.h"
#include "replication/walsender.h"
#include "storage/buf/bufmgr.h"
#include "storage/smgr/fd.h"
#include "storage/freespace.h"
#include "storage/lmgr.h"
#include "storage/predicate.h"
#include "storage/smgr/smgr.h"
#include "storage/standby.h"
#include "utils/datum.h"
#include "utils/inval.h"
#include "utils/relcache.h"
#include "utils/partcache.h"
#include "utils/snapmgr.h"
#include "utils/syscache.h"
#include "access/heapam.h"
#include "utils/guc.h"
#include "vecexecutor/vectorbatch.h"
#include "access/multi_redo_api.h"
#include "gstrace/gstrace_infra.h"
#include "gstrace/access_gstrace.h"
#ifdef PGXC
#include "pgxc/pgxc.h"
#include "pgxc/redistrib.h"
#include "replication/bcm.h"
#endif
static void HeapPageShiftBase(Page page, bool multi, int64 delta);
void HeapXlogCleanOperatorPage(RedoBufferInfo *buffer, void *recorddata, void *blkdata, Size datalen, Size *freespace,
bool repairFragmentation)
{
xl_heap_clean *xlrec = (xl_heap_clean *)recorddata;
Page page = buffer->pageinfo.page;
OffsetNumber *end = NULL;
OffsetNumber *redirected = NULL;
OffsetNumber *nowdead = NULL;
OffsetNumber *nowunused = NULL;
int nredirected;
int ndead;
int nunused;
redirected = (OffsetNumber *)blkdata;
nredirected = xlrec->nredirected;
ndead = xlrec->ndead;
end = (OffsetNumber *)((char *)redirected + datalen);
nowdead = redirected + (nredirected * 2);
nowunused = nowdead + ndead;
nunused = (end - nowunused);
Assert(nunused >= 0);
if (module_logging_is_on(MOD_REDO)) {
DumpPageInfo(page, 0);
}
heap_page_prune_execute(page, redirected, nredirected, nowdead, ndead, nowunused, nunused, repairFragmentation);
if (freespace != NULL) {
*freespace = PageGetHeapFreeSpace(page);
}
if (module_logging_is_on(MOD_REDO)) {
DumpPageInfo(page, buffer->lsn);
}
* Note: we don't worry about updating the page's prunability hints. At
* worst this will cause an extra prune cycle to occur soon.
*/
PageSetLSN(page, buffer->lsn);
}
void HeapXlogFreezeOperatorPage(RedoBufferInfo *buffer, void *recorddata, void *blkdata, Size datalen,
bool isTupleLockUpgrade)
{
xl_heap_freeze *xlrec = (xl_heap_freeze *)recorddata;
Page page = buffer->pageinfo.page;
TransactionId cutoff_xid = xlrec->cutoff_xid;
OffsetNumber *offsets = (OffsetNumber *)blkdata;
OffsetNumber *offsets_end = NULL;
HeapTupleData tuple;
if (datalen > 0) {
offsets_end = (OffsetNumber *)((char *)offsets + datalen);
while (offsets < offsets_end) {
ItemId lp = PageGetItemId(page, *offsets);
tuple.t_data = (HeapTupleHeader)PageGetItem(page, lp);
tuple.t_len = ItemIdGetLength(lp);
HeapTupleCopyBaseFromPage(&tuple, page);
ItemPointerSet(&(tuple.t_self), buffer->blockinfo.blkno, *offsets);
(void)heap_freeze_tuple(&tuple, cutoff_xid, isTupleLockUpgrade ? xlrec->cutoff_multi : InvalidMultiXactId);
offsets++;
}
}
PageSetLSN(page, buffer->lsn);
}
void HeapXlogInvalidOperatorPage(RedoBufferInfo *buffer, void *blkdata, Size datalen)
{
Page page = buffer->pageinfo.page;
if (datalen > 0) {
OffsetNumber *offsets = (OffsetNumber *)blkdata;
OffsetNumber *offsets_end = (OffsetNumber *)((char *)offsets + datalen);
HeapTupleData tuple;
while (offsets < offsets_end) {
ItemId lp = PageGetItemId(page, *offsets);
tuple.t_data = (HeapTupleHeader)PageGetItem(page, lp);
tuple.t_len = ItemIdGetLength(lp);
HeapTupleCopyBaseFromPage(&tuple, page);
ItemPointerSet(&(tuple.t_self), buffer->blockinfo.blkno, *offsets);
heap_invalid_invisible_tuple(&tuple);
offsets++;
}
}
PageSetLSN(page, buffer->lsn);
}
void HeapXlogVisibleOperatorPage(RedoBufferInfo *buffer, void *recorddata)
{
xl_heap_visible *xlrec = (xl_heap_visible *)recorddata;
Page page = buffer->pageinfo.page;
* We don't bump the LSN of the heap page when setting the visibility
* map bit, because that would generate an unworkable volume of
* full-page writes. This exposes us to torn page hazards, but since
* we're not inspecting the existing page contents in any way, we
* don't care.
*
* However, all operations that clear the visibility map bit *do* bump
* the LSN, and those operations will only be replayed if the XLOG LSN
* follows the page LSN. Thus, if the page LSN has advanced past our
* XLOG record's LSN, we mustn't mark the page all-visible, because
* the subsequent update won't be replayed to clear the flag.
*/
PageSetAllVisible(page);
if (IsSegmentFileNode(buffer->blockinfo.rnode)) {
PageSetLSN(page, buffer->lsn);
}
if (xlrec->free_dict && PageIsCompressed(page)) {
(void)PageFreeDict(page);
}
}
void HeapXlogVisibleOperatorVmpage(RedoBufferInfo *vmbuffer, void *recorddata)
{
xl_heap_visible *xlrec = (xl_heap_visible *)recorddata;
Page vmpage = vmbuffer->pageinfo.page;
Relation reln;
* In log_heap_visible, block 0 is vm_buffer, block 1 is heap_buffer.
* the vm and heap must have same relfilenode. so whether use block 0 or 1 is correct for relfilenode
*/
if (PageIsNew(vmpage))
PageInit(vmpage, BLCKSZ, 0);
* XLogReadBufferForRedoExtended locked the buffer. But
* visibilitymap_set will handle locking itself.
*/
LockBuffer(vmbuffer->buf, BUFFER_LOCK_UNLOCK);
reln = CreateFakeRelcacheEntry(vmbuffer->blockinfo.rnode);
Assert(vmbuffer->blockinfo.blkno == HEAPBLK_TO_MAPBLOCK(xlrec->block));
visibilitymap_pin(reln, xlrec->block, &(vmbuffer->buf));
* Don't set the bit if replay has already passed this point.
*
* It might be safe to do this unconditionally; if replay has passed
* this point, we'll replay at least as far this time as we did
* before, and if this bit needs to be cleared, the record responsible
* for doing so should be again replayed, and clear it. For right
* now, out of an abundance of conservatism, we use the same test here
* we did for the heap page. If this results in a dropped bit, no
* real harm is done; and the next VACUUM will fix it.
*/
if (!XLByteLE(vmbuffer->lsn, PageGetLSN(vmpage)))
visibilitymap_set(reln, xlrec->block, InvalidBuffer, vmbuffer->lsn, vmbuffer->buf, xlrec->cutoff_xid, false);
ReleaseBuffer(vmbuffer->buf);
FreeFakeRelcacheEntry(reln);
}
inline static void HeapXlogVisibleOperatorVmbuffer(RedoBufferInfo *vmbuffer, void *recorddata)
{
xl_heap_visible *xlrec = (xl_heap_visible *)recorddata;
Page vmpage = vmbuffer->pageinfo.page;
if (PageIsNew(vmpage))
PageInit(vmpage, BLCKSZ, 0);
Assert(vmbuffer->blockinfo.blkno == HEAPBLK_TO_MAPBLOCK(xlrec->block));
if (!XLByteLE(vmbuffer->lsn, PageGetLSN(vmpage))) {
if (visibilitymap_set_page(vmpage, xlrec->block)) {
PageSetLSN(vmpage, vmbuffer->lsn);
MakeRedoBufferDirty(vmbuffer);
}
}
}
void HeapXlogDeleteOperatorPage(RedoBufferInfo *buffer, void *recorddata, TransactionId recordxid,
bool isTupleLockUpgrade)
{
xl_heap_delete *xlrec = (xl_heap_delete *)recorddata;
Page page = buffer->pageinfo.page;
ItemId lp = NULL;
HeapTupleHeader htup;
ItemPointerData target_tid;
ItemPointerSetBlockNumber(&target_tid, buffer->blockinfo.blkno);
ItemPointerSetOffsetNumber(&target_tid, xlrec->offnum);
OffsetNumber maxoff = PageGetMaxOffsetNumber(page);
if (maxoff >= xlrec->offnum) {
lp = PageGetItemId(page, xlrec->offnum);
} else {
PagePrintErrorInfo(page, "The max offset number is invalid");
}
if (!ItemIdIsNormal(lp))
ereport(PANIC, (errmsg("heap_delete_redo: invalid lp")));
htup = (HeapTupleHeader)PageGetItem(page, lp);
htup->t_infomask &= ~HEAP_XMAX_BITS;
htup->t_infomask2 &= ~(HEAP_XMAX_LOCK_ONLY | HEAP_KEYS_UPDATED);
HeapTupleHeaderClearHotUpdated(htup);
if (isTupleLockUpgrade) {
FixInfomaskFromInfobits(xlrec->infobits_set, &htup->t_infomask, &htup->t_infomask2);
HeapTupleHeaderSetXmax(page, htup, xlrec->xmax);
} else {
htup->t_infomask2 |= HEAP_KEYS_UPDATED;
HeapTupleHeaderSetXmax(page, htup, recordxid);
}
if (!(xlrec->flags & XLH_DELETE_IS_SUPER)) {
if (isTupleLockUpgrade) {
HeapTupleHeaderSetXmax(page, htup, xlrec->xmax);
} else {
HeapTupleHeaderSetXmax(page, htup, recordxid);
}
} else {
HeapTupleHeaderSetXmin(page, htup, FrozenTransactionId);
HeapTupleHeaderSetXmax(page, htup, FrozenTransactionId);
}
HeapTupleHeaderSetCmax(htup, FirstCommandId, false);
PageSetPrunable(page, recordxid);
if (xlrec->flags & XLH_INSERT_ALL_VISIBLE_CLEARED)
PageClearAllVisible(page);
htup->t_ctid = target_tid;
PageSetLSN(page, buffer->lsn);
}
void HeapXlogInsertOperatorPage(RedoBufferInfo *buffer, void *recorddata, bool isinit, void *blkdata, Size datalen,
TransactionId recxid, Size *freespace, bool tde)
{
Pointer rec_data = (Pointer)recorddata;
char *data = (char *)blkdata;
Page page = buffer->pageinfo.page;
TransactionId pd_xid_base = InvalidTransactionId;
xl_heap_insert *xlrec = NULL;
ItemPointerData target_tid;
errno_t rc = EOK;
uint32 newlen;
HeapTupleHeader htup;
xl_heap_header xlhdr;
union {
HeapTupleHeaderData hdr;
char data[MaxHeapTupleSize + sizeof(HeapTupleHeaderData)];
} tbuf;
if (isinit) {
HeapPageHeader phdr;
pd_xid_base = *((TransactionId *)rec_data);
PageInit(page, buffer->pageinfo.pagesize, 0, true);
phdr = (HeapPageHeader)page;
phdr->pd_xid_base = pd_xid_base;
phdr->pd_multi_base = 0;
rec_data += sizeof(TransactionId);
* When it comes to the TDE record, we prefer to remake the init page in TDE format.
* And set TDE flag which on the PAGE to the enabled state.
*/
if (tde) {
phdr->pd_upper -= sizeof(TdePageInfo);
phdr->pd_special -= sizeof(TdePageInfo);
PageSetTDE(page);
}
}
xlrec = (xl_heap_insert *)rec_data;
ItemPointerSetBlockNumber(&target_tid, buffer->blockinfo.blkno);
ItemPointerSetOffsetNumber(&target_tid, xlrec->offnum);
rc = memset_s(&tbuf, sizeof(tbuf), 0, sizeof(tbuf));
securec_check(rc, "\0", "\0");
OffsetNumber maxoff = PageGetMaxOffsetNumber(page);
if (maxoff + 1 < xlrec->offnum) {
PagePrintErrorInfo(page, "The max offset number is invalid");
}
newlen = datalen - SizeOfHeapHeader;
Assert(datalen > SizeOfHeapHeader && newlen <= MaxHeapTupleSize);
rc = memcpy_s((char *)&xlhdr, SizeOfHeapHeader, data, SizeOfHeapHeader);
securec_check(rc, "", "");
data += SizeOfHeapHeader;
htup = &tbuf.hdr;
rc = memset_s((char *)htup, sizeof(HeapTupleHeaderData), 0, sizeof(HeapTupleHeaderData));
securec_check(rc, "\0", "\0");
rc = memcpy_s((char *)htup + offsetof(HeapTupleHeaderData, t_bits), newlen, data, newlen);
securec_check(rc, "\0", "\0");
newlen += offsetof(HeapTupleHeaderData, t_bits);
htup->t_infomask2 = xlhdr.t_infomask2;
htup->t_infomask = xlhdr.t_infomask;
htup->t_hoff = xlhdr.t_hoff;
HeapTupleHeaderSetXmin(page, htup, recxid);
HeapTupleHeaderSetCmin(htup, FirstCommandId);
htup->t_ctid = target_tid;
if (PageAddItem(page, (Item)htup, newlen, xlrec->offnum, true, true) == InvalidOffsetNumber) {
PagePrintErrorInfo(page, "heap_insert_redo: failed to add tuple");
}
if (freespace != NULL) {
*freespace = PageGetHeapFreeSpace(page);
}
PageSetLSN(page, buffer->lsn);
if (xlrec->flags & XLH_INSERT_ALL_VISIBLE_CLEARED)
PageClearAllVisible(page);
}
void HeapXlogMultiInsertOperatorPage(RedoBufferInfo *buffer, const void *recoreddata,
bool isinit, const void *blkdata, Size len,
TransactionId recordxid, Size *freespace, bool tde)
{
Pointer rec_data = (Pointer)recoreddata;
Page page = buffer->pageinfo.page;
BlockNumber blkno = buffer->blockinfo.blkno;
TransactionId pd_xid_base = InvalidTransactionId;
union {
HeapTupleHeaderData hdr;
char data[MaxHeapTupleSize + sizeof(HeapTupleHeaderData)];
} tbuf;
errno_t rc = memset_s(&tbuf, sizeof(tbuf), 0, sizeof(tbuf));
securec_check(rc, "\0", "\0");
if (isinit) {
pd_xid_base = *((TransactionId *)rec_data);
PageInit(page, buffer->pageinfo.pagesize, 0, true);
HeapPageHeader phdr = (HeapPageHeader)page;
phdr->pd_xid_base = pd_xid_base;
phdr->pd_multi_base = 0;
rec_data += sizeof(TransactionId);
* When it comes to the TDE record, we prefer to remake the init page in TDE format.
* And set TDE flag which on the PAGE to the enabled state.
*/
if (tde) {
phdr->pd_upper -= sizeof(TdePageInfo);
phdr->pd_special -= sizeof(TdePageInfo);
PageSetTDE(page);
}
}
xl_heap_multi_insert *xlrec = (xl_heap_multi_insert *)rec_data;
char* newblkdata = (char*)palloc(len);
rc = memcpy_s(newblkdata, len, blkdata, len);
securec_check(rc, "\0", "\0");
char *tupdata = newblkdata;
char *endptr = tupdata + len;
if (xlrec->isCompressed) {
char *cmprsData = (char *)SHORTALIGN(tupdata);
Size cmprSize = *((int16 *)cmprsData);
cmprsData += sizeof(int16);
Assert(isinit);
PageReinitWithDict(page, cmprSize);
rc = memcpy_s((char *)getPageDict(page), (Size)PageGetSpecialSize(page), cmprsData, cmprSize);
securec_check(rc, "\0", "\0");
tupdata = cmprsData + cmprSize;
}
for (uint32 i = 0; i < xlrec->ntuples; i++) {
OffsetNumber offnum, maxoff;
xl_multi_insert_tuple *xlhdr = NULL;
if (isinit)
offnum = FirstOffsetNumber + i;
else
offnum = xlrec->offsets[i];
maxoff = PageGetMaxOffsetNumber(page);
if (maxoff + 1 < offnum) {
PagePrintErrorInfo(page, "heap_multi_insert_redo: The max offset number is invalid");
}
xlhdr = (xl_multi_insert_tuple *)SHORTALIGN(tupdata);
tupdata = ((char *)xlhdr) + SizeOfMultiInsertTuple;
uint32 newlen = xlhdr->datalen;
Assert(newlen <= MaxHeapTupleSize);
HeapTupleHeader htup = &tbuf.hdr;
rc = memset_s((char *)htup, sizeof(HeapTupleHeaderData), 0, sizeof(HeapTupleHeaderData));
securec_check_c(rc, "\0", "\0");
rc = memcpy_s((char *)htup + offsetof(HeapTupleHeaderData, t_bits), newlen, (char *)tupdata, newlen);
securec_check(rc, "\0", "\0");
tupdata += newlen;
newlen += offsetof(HeapTupleHeaderData, t_bits);
htup->t_infomask2 = xlhdr->t_infomask2;
htup->t_infomask = xlhdr->t_infomask;
htup->t_hoff = xlhdr->t_hoff;
HeapTupleHeaderSetXmin(page, htup, recordxid);
HeapTupleHeaderSetCmin(htup, FirstCommandId);
ItemPointerSetBlockNumber(&htup->t_ctid, blkno);
ItemPointerSetOffsetNumber(&htup->t_ctid, offnum);
offnum = PageAddItem(page, (Item)htup, newlen, offnum, true, true);
if (offnum == InvalidOffsetNumber) {
PagePrintErrorInfo(page, "heap_multi_insert_redo: failed to add tuple");
}
}
if (tupdata != endptr) {
PagePrintErrorInfo(page, "heap_multi_insert_redo: total tuple length mismatch");
}
if (freespace != NULL) {
*freespace = PageGetHeapFreeSpace(page);
}
PageSetLSN(page, buffer->lsn);
if (xlrec->flags & XLH_INSERT_ALL_VISIBLE_CLEARED)
PageClearAllVisible(page);
if (newblkdata != NULL) {
pfree(newblkdata);
}
}
void HeapXlogUpdateOperatorOldpage(RedoBufferInfo *buffer, void *recoreddata, bool hot_update, bool isnewinit,
BlockNumber newblk, TransactionId recordxid, bool isTupleLockUpgrade)
{
Page page = buffer->pageinfo.page;
Pointer rec_data = (Pointer)recoreddata;
xl_heap_update *xlrec = NULL;
ItemId lp = NULL;
HeapTupleHeader htup;
ItemPointerData newtid;
if (isnewinit) {
rec_data += sizeof(TransactionId);
}
xlrec = (xl_heap_update *)rec_data;
ItemPointerSet(&newtid, newblk, xlrec->new_offnum);
OffsetNumber maxoff = PageGetMaxOffsetNumber(page);
if (maxoff >= xlrec->old_offnum) {
lp = PageGetItemId(page, xlrec->old_offnum);
} else {
PagePrintErrorInfo(page, "The max offset number is invalid");
}
if (!ItemIdIsNormal(lp)) {
PagePrintErrorInfo(page, "heap_update_redo: invalid lp");
}
htup = (HeapTupleHeader)PageGetItem(page, lp);
htup->t_infomask &= ~HEAP_XMAX_BITS;
htup->t_infomask2 &= ~(HEAP_XMAX_LOCK_ONLY | HEAP_KEYS_UPDATED);
if (hot_update)
HeapTupleHeaderSetHotUpdated(htup);
else
HeapTupleHeaderClearHotUpdated(htup);
if (isTupleLockUpgrade) {
FixInfomaskFromInfobits(xlrec->old_infobits_set, &htup->t_infomask, &htup->t_infomask2);
HeapTupleHeaderSetXmax(page, htup, xlrec->old_xmax);
} else {
htup->t_infomask2 |= HEAP_KEYS_UPDATED;
HeapTupleHeaderSetXmax(page, htup, recordxid);
}
HeapTupleHeaderSetCmax(htup, FirstCommandId, false);
htup->t_ctid = newtid;
PageSetPrunable(page, recordxid);
if (xlrec->flags & XLH_INSERT_ALL_VISIBLE_CLEARED)
PageClearAllVisible(page);
PageHeader oldPhdr = (PageHeader)page;
if (module_logging_is_on(MOD_REDO)) {
ereport(DEBUG4, (errmodule(MOD_REDO), errcode(ERRCODE_LOG),
errmsg("[REDO_LOG_TRACE]heap_xlog_update:,"
"oldPageOldLsn:%lu,OldPageNewLsn:%lu,oldpd_lower:%u, oldpd_upper:%u, "
"oldpd_special:%u,OldPageOffset:%u",
PageGetLSN(page), buffer->lsn, oldPhdr->pd_lower, oldPhdr->pd_upper,
oldPhdr->pd_special, PageGetMaxOffsetNumber(page))));
}
PageSetLSN(page, buffer->lsn);
}
void HeapXlogUpdateOperatorNewpage(RedoBufferInfo *buffer, void *recorddata, bool isinit, void *blkdata, Size datalen,
TransactionId recordxid, Size *freespace, bool isTupleLockUpgrade, bool tde)
{
Page page = buffer->pageinfo.page;
Pointer rec_data = (Pointer)recorddata;
BlockNumber newblk = buffer->blockinfo.blkno;
xl_heap_update *xlrec = NULL;
ItemPointerData newtid;
char *recblkdata = (char *)blkdata;
char *recblkdata_end = NULL;
Size tuplen;
errno_t rc = EOK;
OffsetNumber maxoff;
xl_heap_header xlhdr;
TransactionId pd_xid_base = InvalidTransactionId;
union {
HeapTupleHeaderData hdr;
char data[MaxHeapTupleSize + sizeof(HeapTupleHeaderData)];
} tbuf;
HeapTupleHeader htup;
uint32 newlen;
rc = memset_s(&tbuf, sizeof(tbuf), 0, sizeof(tbuf));
securec_check(rc, "\0", "\0");
if (isinit) {
HeapPageHeader phdr;
pd_xid_base = *((TransactionId *)rec_data);
PageInit(page, buffer->pageinfo.pagesize, 0, true);
phdr = (HeapPageHeader)page;
phdr->pd_xid_base = pd_xid_base;
phdr->pd_multi_base = 0;
rec_data += sizeof(TransactionId);
* When it comes to the TDE record, we prefer to remake the init page in TDE format.
* And set TDE flag which on the PAGE to the enabled state.
*/
if (tde) {
phdr->pd_upper -= sizeof(TdePageInfo);
phdr->pd_special -= sizeof(TdePageInfo);
PageSetTDE(page);
}
}
xlrec = (xl_heap_update *)rec_data;
ItemPointerSet(&newtid, newblk, xlrec->new_offnum);
recblkdata_end = recblkdata + datalen;
maxoff = PageGetMaxOffsetNumber(page);
rc = memcpy_s((char *)&xlhdr, SizeOfHeapHeader, recblkdata, SizeOfHeapHeader);
securec_check(rc, "", "");
recblkdata += SizeOfHeapHeader;
tuplen = recblkdata_end - recblkdata;
PageHeader newphdr = (PageHeader)page;
if (module_logging_is_on(MOD_REDO)) {
ereport(DEBUG4, (errmodule(MOD_REDO), errcode(ERRCODE_LOG),
errmsg("[REDO_LOG_TRACE]heap_xlog_update:,"
"newPageOldLsn:%lu,newPageNewLsn:%lu,newpd_lower:%u, newpd_upper:%u, "
"newpd_special:%u,new_offnum:%u,newPageOffset:%u",
PageGetLSN(page), buffer->lsn, newphdr->pd_lower, newphdr->pd_upper,
newphdr->pd_special, xlrec->new_offnum, PageGetMaxOffsetNumber(page))));
}
if (maxoff + 1 < xlrec->new_offnum) {
PagePrintErrorInfo(page, "heap_update_redo: invalid max offset number");
}
Assert(tuplen <= MaxHeapTupleSize);
htup = &tbuf.hdr;
rc = memset_s((char *)htup, sizeof(HeapTupleHeaderData), 0, sizeof(HeapTupleHeaderData));
securec_check(rc, "\0", "\0");
rc = memcpy_s((char *)htup + offsetof(HeapTupleHeaderData, t_bits), tuplen, recblkdata, tuplen);
securec_check(rc, "\0", "\0");
newlen = offsetof(HeapTupleHeaderData, t_bits) + tuplen;
htup->t_infomask2 = xlhdr.t_infomask2;
htup->t_infomask = xlhdr.t_infomask;
htup->t_hoff = xlhdr.t_hoff;
HeapTupleHeaderSetXmin(page, htup, recordxid);
HeapTupleHeaderSetCmin(htup, FirstCommandId);
if (isTupleLockUpgrade) {
HeapTupleHeaderSetXmax(page, htup, xlrec->new_xmax);
}
htup->t_ctid = newtid;
if (PageAddItem(page, (Item)htup, newlen, xlrec->new_offnum, true, true) == InvalidOffsetNumber) {
PagePrintErrorInfo(page, "heap_update_redo: failed to add tuple");
}
if (xlrec->flags & XLH_UPDATE_NEW_ALL_VISIBLE_CLEARED)
PageClearAllVisible(page);
if (freespace != NULL) {
*freespace = PageGetHeapFreeSpace(page);
}
PageSetLSN(page, buffer->lsn);
}
void HeapXlogLockOperatorPage(RedoBufferInfo *buffer, void *recorddata, bool isTupleLockUpgrade)
{
xl_heap_lock *xlrec = (xl_heap_lock *)recorddata;
Page page = buffer->pageinfo.page;
ItemId lp = NULL;
HeapTupleHeader htup;
OffsetNumber maxoff = PageGetMaxOffsetNumber(page);
if (maxoff >= xlrec->offnum)
lp = PageGetItemId(page, xlrec->offnum);
if (maxoff < xlrec->offnum || !ItemIdIsNormal(lp))
ereport(PANIC, (errmsg("heap_lock_redo: invalid lp")));
htup = (HeapTupleHeader)PageGetItem(page, lp);
htup->t_infomask &= ~HEAP_XMAX_BITS;
htup->t_infomask2 &= ~(HEAP_XMAX_LOCK_ONLY | HEAP_KEYS_UPDATED);
if (isTupleLockUpgrade) {
FixInfomaskFromInfobits(xlrec->infobits_set, &htup->t_infomask, &htup->t_infomask2);
if (xlrec->lock_updated) {
HeapTupleHeaderSetXmax(page, htup, xlrec->locking_xid);
PageSetLSN(page, buffer->lsn);
return;
}
} else {
if (xlrec->xid_is_mxact)
htup->t_infomask |= HEAP_XMAX_IS_MULTI;
if (xlrec->shared_lock)
htup->t_infomask |= HEAP_XMAX_SHARED_LOCK;
else {
htup->t_infomask |= HEAP_XMAX_EXCL_LOCK;
htup->t_infomask2 |= HEAP_KEYS_UPDATED;
}
}
* Clear relevant update flags, but only if the modified infomask says
* there's no update.
*/
if (HEAP_XMAX_IS_LOCKED_ONLY(htup->t_infomask, htup->t_infomask2)) {
HeapTupleHeaderClearHotUpdated(htup);
ItemPointerSet(&htup->t_ctid, buffer->blockinfo.blkno, xlrec->offnum);
}
HeapTupleHeaderSetXmax(page, htup, xlrec->locking_xid);
HeapTupleHeaderSetCmax(htup, FirstCommandId, false);
PageSetLSN(page, buffer->lsn);
}
void HeapXlogInplaceOperatorPage(RedoBufferInfo *buffer, void *recorddata, void *blkdata, Size newlen)
{
xl_heap_inplace *xlrec = (xl_heap_inplace *)recorddata;
Page page = buffer->pageinfo.page;
ItemId lp = NULL;
HeapTupleHeader htup;
uint32 oldlen;
errno_t rc = EOK;
OffsetNumber maxoff;
char *newtup = (char *)blkdata;
if (newtup == NULL)
ereport(PANIC, (errmsg("heap_inplace_redo: no tuple data")));
maxoff = PageGetMaxOffsetNumber(page);
if (maxoff >= xlrec->offnum) {
lp = PageGetItemId(page, xlrec->offnum);
} else {
PagePrintErrorInfo(page, "The max offset number is invalid");
}
if (!ItemIdIsNormal(lp)) {
PagePrintErrorInfo(page, "heap_inplace_redo: invalid lp");
}
htup = (HeapTupleHeader)PageGetItem(page, lp);
oldlen = ItemIdGetLength(lp) - htup->t_hoff;
if (oldlen != newlen)
ereport(PANIC, (errmsg("heap_inplace_redo: wrong tuple length")));
rc = memcpy_s((char *)htup + htup->t_hoff, newlen, newtup, newlen);
securec_check(rc, "\0", "\0");
PageSetLSN(page, buffer->lsn);
}
* @Description: Shift xid base in the page.
* @in: page, heap page
* @in: multi,
* @in: delta, size of change about xid base
*/
static void HeapPageShiftBase(Page page, bool multi, int64 delta)
{
HeapPageHeader phdr = (HeapPageHeader)page;
OffsetNumber offnum, maxoff;
if (delta < 0) {
if (!multi) {
if ((int64)(phdr->pd_xid_base + delta) < 0)
delta = -(int64)(phdr->pd_xid_base);
} else {
if ((int64)(phdr->pd_multi_base + delta) < 0)
delta = -(int64)(phdr->pd_multi_base);
}
}
maxoff = PageGetMaxOffsetNumber(page);
for (offnum = FirstOffsetNumber; offnum <= maxoff; offnum = OffsetNumberNext(offnum)) {
ItemId itemid;
HeapTupleHeader htup;
itemid = PageGetItemId(page, offnum);
if (!ItemIdIsNormal(itemid))
continue;
htup = (HeapTupleHeader)PageGetItem(page, itemid);
if (!multi) {
if (!HeapTupleHeaderXminFrozen(htup) && TransactionIdIsNormal(htup->t_choice.t_heap.t_xmin)) {
Assert((uint32)(htup->t_choice.t_heap.t_xmin - delta) >= FirstNormalTransactionId);
Assert((uint32)(htup->t_choice.t_heap.t_xmin - delta) <= MaxShortTransactionId);
htup->t_choice.t_heap.t_xmin -= delta;
}
if (TransactionIdIsNormal(htup->t_choice.t_heap.t_xmax) && !(htup->t_infomask & HEAP_XMAX_IS_MULTI)) {
Assert((uint32)(htup->t_choice.t_heap.t_xmax - delta) >= FirstNormalTransactionId);
Assert((uint32)(htup->t_choice.t_heap.t_xmax - delta) <= MaxShortTransactionId);
htup->t_choice.t_heap.t_xmax -= delta;
}
} else {
if (TransactionIdIsNormal(htup->t_choice.t_heap.t_xmax) && (htup->t_infomask & HEAP_XMAX_IS_MULTI)) {
Assert((uint32)(htup->t_choice.t_heap.t_xmax - delta) >= FirstNormalTransactionId);
Assert((uint32)(htup->t_choice.t_heap.t_xmax - delta) <= MaxShortTransactionId);
htup->t_choice.t_heap.t_xmax -= delta;
}
}
}
if (!multi)
phdr->pd_xid_base += delta;
else
phdr->pd_multi_base += delta;
ereport(DEBUG1, (errmsg("The page xid_base has changed to %lu ", phdr->pd_xid_base)));
}
void HeapXlogBaseShiftOperatorPage(RedoBufferInfo *buffer, void *recorddata)
{
xl_heap_base_shift *xlrec = (xl_heap_base_shift *)recorddata;
Page page = buffer->pageinfo.page;
HeapPageShiftBase(page, xlrec->multi, xlrec->delta);
PageSetLSN(page, buffer->lsn);
}
static XLogRecParseState *HeapXlogInsertParseBlock(XLogReaderState *record, uint32 *blocknum)
{
xl_heap_insert *xlrec = NULL;
bool isinit = (XLogRecGetInfo(record) & XLOG_HEAP_INIT_PAGE) != 0;
Pointer rec_data;
XLogRecParseState *recordstatehead = NULL;
XLogRecParseState *blockstate = NULL;
*blocknum = 1;
XLogParseBufferAllocListFunc(record, &recordstatehead, NULL);
if (recordstatehead == NULL) {
return NULL;
}
XLogRecSetBlockDataState(record, HEAP_INSERT_ORIG_BLOCK_NUM, recordstatehead);
rec_data = (Pointer)XLogRecGetData(record);
if (isinit) {
rec_data += sizeof(TransactionId);
}
xlrec = (xl_heap_insert *)rec_data;
if (xlrec->flags & XLH_INSERT_ALL_VISIBLE_CLEARED) {
(*blocknum)++;
XLogParseBufferAllocListFunc(record, &blockstate, recordstatehead);
if (blockstate == NULL) {
return NULL;
}
XLogRecSetVmBlockState(record, HEAP_INSERT_ORIG_BLOCK_NUM, blockstate);
}
return recordstatehead;
}
static XLogRecParseState *HeapXlogDeleteParseBlock(XLogReaderState *record, uint32 *blocknum)
{
xl_heap_delete *xlrec = (xl_heap_delete *)XLogRecGetData(record);
XLogRecParseState *recordstatehead = NULL;
XLogRecParseState *blockstate = NULL;
*blocknum = 1;
XLogParseBufferAllocListFunc(record, &recordstatehead, NULL);
if (recordstatehead == NULL) {
return NULL;
}
XLogRecSetBlockDataState(record, HEAP_DELETE_ORIG_BLOCK_NUM, recordstatehead);
if (xlrec->flags & XLH_INSERT_ALL_VISIBLE_CLEARED) {
(*blocknum)++;
XLogParseBufferAllocListFunc(record, &blockstate, recordstatehead);
if (blockstate == NULL) {
return NULL;
}
XLogRecSetVmBlockState(record, HEAP_DELETE_ORIG_BLOCK_NUM, blockstate);
}
return recordstatehead;
}
* Handles UPDATE and HOT_UPDATE
*/
static XLogRecParseState *HeapXlogUpdateParseBlock(XLogReaderState *record, uint32 *blocknum)
{
xl_heap_update *xlrec = NULL;
bool isinit = (XLogRecGetInfo(record) & XLOG_HEAP_INIT_PAGE) != 0;
Pointer rec_data;
XLogRecParseState *recordstatehead = NULL;
XLogRecParseState *blockstate = NULL;
BlockNumber newblk, oldblk;
XLogRecGetBlockTag(record, HEAP_UPDATE_NEW_BLOCK_NUM, NULL, NULL, &newblk);
if (!XLogRecGetBlockTag(record, HEAP_UPDATE_OLD_BLOCK_NUM, NULL, NULL, &oldblk)) {
oldblk = newblk;
}
*blocknum = 1;
XLogParseBufferAllocListFunc(record, &recordstatehead, NULL);
if (recordstatehead == NULL) {
return NULL;
}
XLogRecSetBlockDataState(record, HEAP_UPDATE_NEW_BLOCK_NUM, recordstatehead);
XLogRecSetAuxiBlkNumState(&recordstatehead->blockparse.extra_rec.blockdatarec, oldblk, InvalidForkNumber);
rec_data = (Pointer)XLogRecGetData(record);
if (isinit) {
rec_data += sizeof(TransactionId);
}
xlrec = (xl_heap_update *)rec_data;
if (oldblk != newblk) {
Assert(!(((XLogRecGetInfo(record) & ~XLR_INFO_MASK) & XLOG_HEAP_OPMASK) == XLOG_HEAP_HOT_UPDATE));
(*blocknum)++;
XLogParseBufferAllocListFunc(record, &blockstate, recordstatehead);
if (blockstate == NULL) {
return NULL;
}
XLogRecSetBlockDataState(record, HEAP_UPDATE_OLD_BLOCK_NUM, blockstate);
XLogRecSetAuxiBlkNumState(&blockstate->blockparse.extra_rec.blockdatarec, newblk, InvalidForkNumber);
if (xlrec->flags & XLH_UPDATE_OLD_ALL_VISIBLE_CLEARED) {
(*blocknum)++;
XLogParseBufferAllocListFunc(record, &blockstate, recordstatehead);
if (blockstate == NULL) {
return NULL;
}
XLogRecSetVmBlockState(record, HEAP_UPDATE_OLD_BLOCK_NUM, blockstate);
}
}
if ((xlrec->flags & XLH_UPDATE_NEW_ALL_VISIBLE_CLEARED) ||
((oldblk == newblk) && (xlrec->flags & XLH_UPDATE_OLD_ALL_VISIBLE_CLEARED))) {
(*blocknum)++;
XLogParseBufferAllocListFunc(record, &blockstate, recordstatehead);
if (blockstate == NULL) {
return NULL;
}
XLogRecSetVmBlockState(record, HEAP_UPDATE_NEW_BLOCK_NUM, blockstate);
}
return recordstatehead;
}
static XLogRecParseState *HeapXlogBaseShiftParseBlock(XLogReaderState *record, uint32 *blocknum)
{
XLogRecParseState *recordstatehead = NULL;
*blocknum = 1;
XLogParseBufferAllocListFunc(record, &recordstatehead, NULL);
if (recordstatehead == NULL) {
return NULL;
}
XLogRecSetBlockDataState(record, HEAP_BASESHIFT_ORIG_BLOCK_NUM, recordstatehead, BLOCK_DATA_MAIN_DATA_TYPE, true);
return recordstatehead;
}
static XLogRecParseState *HeapXlogNewpageParseBlock(XLogReaderState *record, uint32 *blocknum)
{
XLogRecParseState *recordstatehead = NULL;
*blocknum = 1;
XLogParseBufferAllocListFunc(record, &recordstatehead, NULL);
if (recordstatehead == NULL) {
return NULL;
}
XLogRecSetBlockDataState(record, HEAP_NEWPAGE_ORIG_BLOCK_NUM, recordstatehead);
return recordstatehead;
}
static XLogRecParseState *HeapXlogLockParseBlock(XLogReaderState *record, uint32 *blocknum)
{
XLogRecParseState *recordstatehead = NULL;
*blocknum = 1;
XLogParseBufferAllocListFunc(record, &recordstatehead, NULL);
if (recordstatehead == NULL) {
return NULL;
}
XLogRecSetBlockDataState(record, HEAP_LOCK_ORIG_BLOCK_NUM, recordstatehead, BLOCK_DATA_MAIN_DATA_TYPE, true);
return recordstatehead;
}
static XLogRecParseState *HeapXlogInplaceParseBlock(XLogReaderState *record, uint32 *blocknum)
{
XLogRecParseState *recordstatehead = NULL;
*blocknum = 1;
XLogParseBufferAllocListFunc(record, &recordstatehead, NULL);
if (recordstatehead == NULL) {
return NULL;
}
XLogRecSetBlockDataState(record, HEAP_INPLACE_ORIG_BLOCK_NUM, recordstatehead);
return recordstatehead;
}
XLogRecParseState *HeapRedoParseToBlock(XLogReaderState *record, uint32 *blocknum)
{
uint8 info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
XLogRecParseState *recordblockstate = NULL;
*blocknum = 0;
* These operations don't overwrite MVCC data so no conflict processing is
* required. The ones in heap2 rmgr do.
*/
switch (info & XLOG_HEAP_OPMASK) {
case XLOG_HEAP_INSERT:
recordblockstate = HeapXlogInsertParseBlock(record, blocknum);
break;
case XLOG_HEAP_DELETE:
recordblockstate = HeapXlogDeleteParseBlock(record, blocknum);
break;
case XLOG_HEAP_UPDATE:
case XLOG_HEAP_HOT_UPDATE:
recordblockstate = HeapXlogUpdateParseBlock(record, blocknum);
break;
case XLOG_HEAP_BASE_SHIFT:
recordblockstate = HeapXlogBaseShiftParseBlock(record, blocknum);
break;
case XLOG_HEAP_NEWPAGE:
recordblockstate = HeapXlogNewpageParseBlock(record, blocknum);
break;
case XLOG_HEAP_LOCK:
recordblockstate = HeapXlogLockParseBlock(record, blocknum);
break;
case XLOG_HEAP_INPLACE:
recordblockstate = HeapXlogInplaceParseBlock(record, blocknum);
break;
default:
ereport(PANIC, (errmsg("HeapRedoParseToBlock: unknown op code %u", info)));
}
return recordblockstate;
}
static XLogRecParseState *HeapXlogFreezeParseBlock(XLogReaderState *record, uint32 *blocknum)
{
XLogRecParseState *recordstatehead = NULL;
*blocknum = 1;
XLogParseBufferAllocListFunc(record, &recordstatehead, NULL);
if (recordstatehead == NULL) {
return NULL;
}
XLogRecSetBlockDataState(record, HEAP_FREEZE_ORIG_BLOCK_NUM, recordstatehead, BLOCK_DATA_MAIN_DATA_TYPE, true);
return recordstatehead;
}
static XLogRecParseState *HeapXlogInvalidParseBlock(XLogReaderState *record, uint32 *blocknum)
{
*blocknum = 1;
XLogRecParseState *recordstatehead = NULL;
XLogParseBufferAllocListFunc(record, &recordstatehead, NULL);
if (recordstatehead == NULL) {
return NULL;
}
XLogRecSetBlockDataState(record, HEAP_FREEZE_ORIG_BLOCK_NUM, recordstatehead, BLOCK_DATA_MAIN_DATA_TYPE, true);
return recordstatehead;
}
static XLogRecParseState *HeapXlogCleanParseBlock(XLogReaderState *record, uint32 *blocknum)
{
XLogRecParseState *recordstatehead = NULL;
*blocknum = 1;
XLogParseBufferAllocListFunc(record, &recordstatehead, NULL);
if (recordstatehead == NULL) {
return NULL;
}
XLogRecSetBlockDataState(record, HEAP_CLEAN_ORIG_BLOCK_NUM, recordstatehead, BLOCK_DATA_MAIN_DATA_TYPE, true);
return recordstatehead;
}
static XLogRecParseState *HeapXlogCleanupInfoParseBlock(XLogReaderState *record, uint32 *blocknum)
{
XLogRecParseState *recordstatehead = NULL;
RelFileNodeOld *rnode = NULL;
ForkNumber forknum = MAIN_FORKNUM;
BlockNumber blkno = InvalidBlockNumber;
(*blocknum)++;
XLogParseBufferAllocListFunc(record, &recordstatehead, NULL);
xl_heap_cleanup_info *xlrec = (xl_heap_cleanup_info *)XLogRecGetData(record);
rnode = &(xlrec->node);
forknum = MAIN_FORKNUM;
RelFileNode tmp_node;
RelFileNodeCopy(tmp_node, *rnode, (int2)XLogRecGetBucketId(record));
tmp_node.opt = 0;
RelFileNodeForkNum filenode = RelFileNodeForkNumFill(&tmp_node, InvalidBackendId, forknum, blkno);
XLogRecSetBlockCommonState(record, BLOCK_DATA_CLEANUP_TYPE, filenode, recordstatehead);
wal_rec_set_clean_up_info_state(&(recordstatehead->blockparse.extra_rec.clean_up_info), xlrec->latestRemovedXid);
return recordstatehead;
}
static XLogRecParseState *HeapXlogVisibleParseBlock(XLogReaderState *record, uint32 *blocknum)
{
XLogRecParseState *recordstatehead = NULL;
XLogRecParseState *blockstate = NULL;
*blocknum = 1;
XLogParseBufferAllocListFunc(record, &recordstatehead, NULL);
if (recordstatehead == NULL) {
return NULL;
}
XLogRecSetBlockDataState(record, HEAP_VISIBLE_VM_BLOCK_NUM, recordstatehead, BLOCK_DATA_MAIN_DATA_TYPE, true);
if (XLogRecHasBlockRef(record, HEAP_VISIBLE_DATA_BLOCK_NUM)) {
(*blocknum)++;
XLogParseBufferAllocListFunc(record, &blockstate, recordstatehead);
if (blockstate == NULL) {
return NULL;
}
XLogRecSetBlockDataState(record, HEAP_VISIBLE_DATA_BLOCK_NUM, blockstate, BLOCK_DATA_MAIN_DATA_TYPE, true);
}
return recordstatehead;
}
static XLogRecParseState *HeapXlogBcmParseBlock(XLogReaderState *record, uint32 *blocknum)
{
xl_heap_bcm *xlrec = (xl_heap_bcm *)XLogRecGetData(record);
int col = xlrec->col;
XLogRecParseState *recordstatehead = NULL;
XLogRecParseState *blockstate = NULL;
BlockNumber curBcmBlock;
if (SUPPORT_COLUMN_BATCH) {
*blocknum = 1;
XLogParseBufferAllocListFunc(record, &recordstatehead, NULL);
if (recordstatehead == NULL) {
return NULL;
}
blockstate = recordstatehead;
curBcmBlock = HEAPBLK_TO_BCMBLOCK(xlrec->block);
RelFileNode rnode;
RelFileNodeCopy(rnode, xlrec->node, XLogRecGetBucketId(record));
RelFileNodeForkNum filenode = RelFileNodeForkNumFill(&rnode, InvalidBackendId, col, curBcmBlock);
XLogRecSetBlockCommonState(record, BLOCK_DATA_BCM_TYPE, filenode, blockstate);
XLogRecSetNewCuState(&recordstatehead->blockparse.extra_rec.blocknewcu, XLogRecGetData(record),
XLogRecGetDataLen(record));
}
return recordstatehead;
}
static XLogRecParseState *HeapXlogMultiInsertParseBlock(XLogReaderState *record, uint32 *blocknum)
{
xl_heap_multi_insert *xlrec = NULL;
bool isinit = (XLogRecGetInfo(record) & XLOG_HEAP_INIT_PAGE) != 0;
Pointer rec_data;
XLogRecParseState *recordstatehead = NULL;
XLogRecParseState *blockstate = NULL;
*blocknum = 1;
;
XLogParseBufferAllocListFunc(record, &recordstatehead, NULL);
if (recordstatehead == NULL) {
return NULL;
}
XLogRecSetBlockDataState(record, HEAP_MULTI_INSERT_ORIG_BLOCK_NUM, recordstatehead);
rec_data = (Pointer)XLogRecGetData(record);
if (isinit) {
rec_data += sizeof(TransactionId);
}
xlrec = (xl_heap_multi_insert *)rec_data;
if (xlrec->flags & XLH_INSERT_ALL_VISIBLE_CLEARED) {
(*blocknum)++;
XLogParseBufferAllocListFunc(record, &blockstate, recordstatehead);
if (blockstate == NULL) {
return NULL;
}
XLogRecSetVmBlockState(record, HEAP_MULTI_INSERT_ORIG_BLOCK_NUM, blockstate);
}
return recordstatehead;
}
static XLogRecParseState *HeapXlogLogicalNewPageParseBlock(XLogReaderState *record, uint32 *blocknum)
{
XLogRecParseState *recordstatehead = NULL;
*blocknum = 0;
if (SUPPORT_COLUMN_BATCH) {
*blocknum = 1;
XLogParseBufferAllocListFunc(record, &recordstatehead, NULL);
if (recordstatehead == NULL) {
return NULL;
}
xl_heap_logical_newpage *xlrec = (xl_heap_logical_newpage *)XLogRecGetData(record);
RelFileNode rnode;
RelFileNodeCopy(rnode, xlrec->node, XLogRecGetBucketId(record));
RelFileNodeForkNum filenode = RelFileNodeForkNumFill(&rnode, InvalidBackendId, xlrec->blkno, xlrec->attid);
XLogRecSetBlockCommonState(record, BLOCK_DATA_NEWCU_TYPE, filenode, recordstatehead);
XLogRecSetNewCuState(&recordstatehead->blockparse.extra_rec.blocknewcu, XLogRecGetData(record),
XLogRecGetDataLen(record));
}
return recordstatehead;
}
XLogRecParseState *Heap2RedoParseIoBlock(XLogReaderState *record, uint32 *blocknum)
{
uint8 info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
XLogRecParseState *recordblockstate = NULL;
*blocknum = 0;
switch (info & XLOG_HEAP_OPMASK) {
case XLOG_HEAP2_FREEZE:
recordblockstate = HeapXlogFreezeParseBlock(record, blocknum);
break;
case XLOG_HEAP2_CLEAN:
recordblockstate = HeapXlogCleanParseBlock(record, blocknum);
break;
case XLOG_HEAP2_CLEANUP_INFO:
recordblockstate = HeapXlogCleanupInfoParseBlock(record, blocknum);
break;
case XLOG_HEAP2_VISIBLE:
recordblockstate = HeapXlogVisibleParseBlock(record, blocknum);
break;
case XLOG_HEAP2_BCM:
recordblockstate = HeapXlogBcmParseBlock(record, blocknum);
break;
case XLOG_HEAP2_MULTI_INSERT:
recordblockstate = HeapXlogMultiInsertParseBlock(record, blocknum);
break;
case XLOG_HEAP2_LOGICAL_NEWPAGE:
recordblockstate = HeapXlogLogicalNewPageParseBlock(record, blocknum);
break;
default:
ereport(PANIC, (errmsg("Heap2RedoParseIoBlock: unknown op code %u", info)));
}
return recordblockstate;
}
static void HeapXlogInsertBlock(XLogBlockHead *blockhead, XLogBlockDataParse *blockdatarec, RedoBufferInfo *bufferinfo)
{
bool isinit = (XLogBlockHeadGetInfo(blockhead) & XLOG_HEAP_INIT_PAGE) != 0;
bool tde = ((blockdatarec->blockhead.cur_block_id) & BKID_HAS_TDE_PAGE) != 0;
TransactionId recordxid = XLogBlockHeadGetXid(blockhead);
XLogBlockDataParse *datadecode = blockdatarec;
XLogRedoAction action;
action = XLogCheckBlockDataRedoAction(datadecode, bufferinfo);
if (action == BLK_NEEDS_REDO) {
Size blkdatalen;
char *blkdata = NULL;
char *maindata = XLogBlockDataGetMainData(datadecode, NULL);
blkdata = XLogBlockDataGetBlockData(datadecode, &blkdatalen);
Assert(blkdata != NULL);
HeapXlogInsertOperatorPage(bufferinfo, maindata, isinit, (void *)blkdata, blkdatalen, recordxid, NULL, tde);
MakeRedoBufferDirty(bufferinfo);
}
}
static void HeapXlogClearVmBlock(XLogBlockVmParse *blockvm, RedoBufferInfo *bufferinfo)
{
if (PageIsNew(bufferinfo->pageinfo.page)) {
PageInit(bufferinfo->pageinfo.page, BLCKSZ, 0);
}
visibilitymap_clear_buffer(bufferinfo, blockvm->heapBlk);
}
static void HeapXlogDeleteBlock(XLogBlockHead *blockhead, XLogBlockDataParse *blockdatarec, RedoBufferInfo *bufferinfo)
{
TransactionId recordxid = XLogBlockHeadGetXid(blockhead);
XLogBlockDataParse *datadecode = blockdatarec;
XLogRedoAction action;
action = XLogCheckBlockDataRedoAction(datadecode, bufferinfo);
if (action == BLK_NEEDS_REDO) {
char *maindata = XLogBlockDataGetMainData(datadecode, NULL);
bool isTupleLockUpgrad = (XLogBlockHeadGetInfo(blockhead) & XLOG_TUPLE_LOCK_UPGRADE_FLAG) != 0;
HeapXlogDeleteOperatorPage(bufferinfo, (void *)maindata, recordxid, isTupleLockUpgrad);
MakeRedoBufferDirty(bufferinfo);
}
}
static void HeapXlogUpdateBlock(XLogBlockHead *blockhead, XLogBlockDataParse *blockdatarec, RedoBufferInfo *bufferinfo)
{
bool isinit = (XLogBlockHeadGetInfo(blockhead) & XLOG_HEAP_INIT_PAGE) != 0;
bool hot_update = (((XLogBlockHeadGetInfo(blockhead) & ~XLR_INFO_MASK) & XLOG_HEAP_OPMASK) == XLOG_HEAP_HOT_UPDATE);
bool tde = ((blockdatarec->blockhead.cur_block_id) & BKID_HAS_TDE_PAGE) != 0;
TransactionId recordxid = XLogBlockHeadGetXid(blockhead);
XLogBlockDataParse *datadecode = blockdatarec;
bool isTupleLockUpgrade = (XLogBlockHeadGetInfo(blockhead) & XLOG_TUPLE_LOCK_UPGRADE_FLAG) != 0;
XLogRedoAction action;
action = XLogCheckBlockDataRedoAction(datadecode, bufferinfo);
if (action == BLK_NEEDS_REDO) {
char *maindata = XLogBlockDataGetMainData(datadecode, NULL);
if (XLogBlockDataGetBlockId(datadecode) == HEAP_UPDATE_NEW_BLOCK_NUM) {
Size blkdatalen;
char *blkdata = NULL;
BlockNumber oldblk = XLogBlockDataGetAuxiBlock1(datadecode);
if (oldblk == bufferinfo->blockinfo.blkno) {
HeapXlogUpdateOperatorOldpage(bufferinfo, (void *)maindata, hot_update, isinit, oldblk,
recordxid, isTupleLockUpgrade);
}
blkdata = XLogBlockDataGetBlockData(datadecode, &blkdatalen);
Assert(blkdata != NULL);
HeapXlogUpdateOperatorNewpage(bufferinfo, (void *)maindata, isinit, (void *)blkdata, blkdatalen, recordxid,
NULL, isTupleLockUpgrade, tde);
} else {
BlockNumber newblk = XLogBlockDataGetAuxiBlock1(datadecode);
HeapXlogUpdateOperatorOldpage(bufferinfo, (void *)maindata, hot_update, isinit, newblk, recordxid,
isTupleLockUpgrade);
}
MakeRedoBufferDirty(bufferinfo);
}
}
static void HeapXlogBaseShiftBlock(XLogBlockHead *blockhead, XLogBlockDataParse *blockdatarec,
RedoBufferInfo *bufferinfo)
{
XLogBlockDataParse *datadecode = blockdatarec;
XLogRedoAction action;
action = XLogCheckBlockDataRedoAction(datadecode, bufferinfo);
if (action == BLK_NEEDS_REDO) {
char *maindata = XLogBlockDataGetMainData(datadecode, NULL);
HeapXlogBaseShiftOperatorPage(bufferinfo, (void *)maindata);
MakeRedoBufferDirty(bufferinfo);
}
}
static void HeapXlogNewpageBlock(XLogBlockHead *blockhead, XLogBlockDataParse *blockdatarec, RedoBufferInfo *bufferinfo)
{
XLogBlockDataParse *datadecode = blockdatarec;
XLogRedoAction action;
action = XLogCheckBlockDataRedoAction(datadecode, bufferinfo);
if (action != BLK_RESTORED)
ereport(ERROR, (errcode(ERRCODE_DATA_CORRUPTED),
errmsg("HeapXlogNewpageBlock unexpected result when restoring backup block")));
}
static void HeapXlogLockBlock(XLogBlockHead *blockhead, XLogBlockDataParse *blockdatarec, RedoBufferInfo *bufferinfo)
{
XLogBlockDataParse *datadecode = blockdatarec;
XLogRedoAction action;
action = XLogCheckBlockDataRedoAction(datadecode, bufferinfo);
if (action == BLK_NEEDS_REDO) {
char *maindata = XLogBlockDataGetMainData(datadecode, NULL);
bool isTupleLockUpgrade = (XLogBlockHeadGetInfo(blockhead) & XLOG_TUPLE_LOCK_UPGRADE_FLAG) != 0;
HeapXlogLockOperatorPage(bufferinfo, (void *)maindata, isTupleLockUpgrade);
MakeRedoBufferDirty(bufferinfo);
}
}
static void HeapXlogInplaceBlock(XLogBlockHead *blockhead, XLogBlockDataParse *blockdatarec, RedoBufferInfo *bufferinfo)
{
XLogBlockDataParse *datadecode = blockdatarec;
XLogRedoAction action;
action = XLogCheckBlockDataRedoAction(datadecode, bufferinfo);
if (action == BLK_NEEDS_REDO) {
Size blkdatalen;
char *blkdata = XLogBlockDataGetBlockData(datadecode, &blkdatalen);
char *maindata = XLogBlockDataGetMainData(datadecode, NULL);
Assert(blkdata != NULL);
HeapXlogInplaceOperatorPage(bufferinfo, (void *)maindata, (void *)blkdata, blkdatalen);
MakeRedoBufferDirty(bufferinfo);
}
}
void HeapRedoDataBlock(XLogBlockHead *blockhead, XLogBlockDataParse *blockdatarec, RedoBufferInfo *bufferinfo)
{
uint8 info = XLogBlockHeadGetInfo(blockhead) & ~XLR_INFO_MASK;
switch (info & XLOG_HEAP_OPMASK) {
case XLOG_HEAP_INSERT:
HeapXlogInsertBlock(blockhead, blockdatarec, bufferinfo);
break;
case XLOG_HEAP_DELETE:
HeapXlogDeleteBlock(blockhead, blockdatarec, bufferinfo);
break;
case XLOG_HEAP_UPDATE:
case XLOG_HEAP_HOT_UPDATE:
HeapXlogUpdateBlock(blockhead, blockdatarec, bufferinfo);
break;
case XLOG_HEAP_BASE_SHIFT:
HeapXlogBaseShiftBlock(blockhead, blockdatarec, bufferinfo);
break;
case XLOG_HEAP_NEWPAGE:
HeapXlogNewpageBlock(blockhead, blockdatarec, bufferinfo);
break;
case XLOG_HEAP_LOCK:
HeapXlogLockBlock(blockhead, blockdatarec, bufferinfo);
break;
case XLOG_HEAP_INPLACE:
HeapXlogInplaceBlock(blockhead, blockdatarec, bufferinfo);
break;
default:
ereport(PANIC, (errmsg("HeapRedoDataBlock: unknown op code %u", info)));
}
}
void HeapRedoVmBlock(XLogBlockHead *blockhead, XLogBlockVmParse *blockvmrec, RedoBufferInfo *bufferinfo)
{
uint8 info = XLogBlockHeadGetInfo(blockhead) & ~XLR_INFO_MASK;
XLogBlockVmParse *blockvm = blockvmrec;
switch (info & XLOG_HEAP_OPMASK) {
case XLOG_HEAP_INSERT:
case XLOG_HEAP_DELETE:
case XLOG_HEAP_UPDATE:
case XLOG_HEAP_HOT_UPDATE:
HeapXlogClearVmBlock(blockvm, bufferinfo);
break;
default:
ereport(PANIC, (errmsg("HeapRedoVmBlock: unknown op code %u", info)));
}
}
static void HeapXlogFreezeBlock(XLogBlockHead *blockhead, XLogBlockDataParse *blockdatarec, RedoBufferInfo *bufferinfo)
{
XLogBlockDataParse *datadecode = blockdatarec;
XLogRedoAction action;
action = XLogCheckBlockDataRedoAction(datadecode, bufferinfo);
if (action == BLK_NEEDS_REDO) {
Size blkdatalen;
char *blkdata = NULL;
char *maindata = XLogBlockDataGetMainData(datadecode, NULL);
blkdata = XLogBlockDataGetBlockData(datadecode, &blkdatalen);
Assert(blkdata != NULL);
HeapXlogFreezeOperatorPage(bufferinfo, (void *)maindata, (void *)blkdata, blkdatalen, false);
MakeRedoBufferDirty(bufferinfo);
}
}
static void HeapXlogInvalidBlock(XLogBlockHead *blockhead, XLogBlockDataParse *blockdatarec, RedoBufferInfo *bufferinfo)
{
XLogBlockDataParse *datadecode = blockdatarec;
XLogRedoAction action = XLogCheckBlockDataRedoAction(datadecode, bufferinfo);
if (action == BLK_NEEDS_REDO) {
Size blkdatalen;
char *blkdata = XLogBlockDataGetBlockData(datadecode, &blkdatalen);
Assert(blkdata != NULL);
HeapXlogInvalidOperatorPage(bufferinfo, (void *)blkdata, blkdatalen);
MakeRedoBufferDirty(bufferinfo);
}
}
static void HeapXlogCleanBlock(XLogBlockHead *blockhead, XLogBlockDataParse *blockdatarec, RedoBufferInfo *bufferinfo)
{
XLogBlockDataParse *datadecode = blockdatarec;
XLogRedoAction action;
bool repairFragmentation = true;
action = XLogCheckBlockDataRedoAction(datadecode, bufferinfo);
if ((XLogBlockHeadGetInfo(blockhead) & XLOG_HEAP2_NO_REPAIR_PAGE) != 0) {
repairFragmentation = false;
}
if (action == BLK_NEEDS_REDO) {
Size blkdatalen;
char *blkdata = NULL;
char *maindata = XLogBlockDataGetMainData(datadecode, NULL);
blkdata = XLogBlockDataGetBlockData(datadecode, &blkdatalen);
Assert(blkdata != NULL);
HeapXlogCleanOperatorPage(bufferinfo, (void *)maindata, (void *)blkdata, blkdatalen, NULL, repairFragmentation);
MakeRedoBufferDirty(bufferinfo);
}
}
static void HeapXlogVisibleBlock(XLogBlockHead *blockhead, XLogBlockDataParse *blockdatarec, RedoBufferInfo *bufferinfo)
{
XLogBlockDataParse *datadecode = blockdatarec;
XLogRedoAction action;
action = XLogCheckBlockDataRedoAction(datadecode, bufferinfo);
if (action == BLK_NEEDS_REDO) {
char *maindata = XLogBlockDataGetMainData(datadecode, NULL);
if (XLogBlockDataGetBlockId(datadecode) == HEAP_VISIBLE_VM_BLOCK_NUM) {
HeapXlogVisibleOperatorVmbuffer(bufferinfo, (void *)maindata);
} else {
HeapXlogVisibleOperatorPage(bufferinfo, (void *)maindata);
}
MakeRedoBufferDirty(bufferinfo);
}
}
static void HeapXlogMultiInsertBlock(XLogBlockHead *blockhead, XLogBlockDataParse *blockdatarec,
RedoBufferInfo *bufferinfo)
{
bool isinit = (XLogBlockHeadGetInfo(blockhead) & XLOG_HEAP_INIT_PAGE) != 0;
bool tde = ((blockdatarec->blockhead.cur_block_id) & BKID_HAS_TDE_PAGE) != 0;
TransactionId recordxid = XLogBlockHeadGetXid(blockhead);
XLogBlockDataParse *datadecode = blockdatarec;
XLogRedoAction action;
action = XLogCheckBlockDataRedoAction(datadecode, bufferinfo);
if (action == BLK_NEEDS_REDO) {
char *maindata = XLogBlockDataGetMainData(datadecode, NULL);
Size blkdatalen;
char *blkdata = NULL;
blkdata = XLogBlockDataGetBlockData(datadecode, &blkdatalen);
Assert(blkdata != NULL);
HeapXlogMultiInsertOperatorPage(bufferinfo, (void *)maindata, isinit, (void *)blkdata, blkdatalen, recordxid,
NULL, tde);
MakeRedoBufferDirty(bufferinfo);
}
}
void Heap2RedoDataBlock(XLogBlockHead *blockhead, XLogBlockDataParse *blockdatarec, RedoBufferInfo *bufferinfo)
{
uint8 info = XLogBlockHeadGetInfo(blockhead) & ~XLR_INFO_MASK;
switch (info & XLOG_HEAP_OPMASK) {
case XLOG_HEAP2_FREEZE:
HeapXlogFreezeBlock(blockhead, blockdatarec, bufferinfo);
break;
case XLOG_HEAP2_CLEAN:
HeapXlogCleanBlock(blockhead, blockdatarec, bufferinfo);
break;
case XLOG_HEAP2_VISIBLE:
HeapXlogVisibleBlock(blockhead, blockdatarec, bufferinfo);
break;
case XLOG_HEAP2_MULTI_INSERT:
HeapXlogMultiInsertBlock(blockhead, blockdatarec, bufferinfo);
break;
default:
ereport(PANIC, (errmsg("heap2_redo_block: unknown op code %u", info)));
}
}
void Heap2RedoVmBlock(XLogBlockHead *blockhead, XLogBlockVmParse *blockvmrec, RedoBufferInfo *bufferinfo)
{
uint8 info = XLogBlockHeadGetInfo(blockhead) & ~XLR_INFO_MASK;
XLogBlockVmParse *blockvm = blockvmrec;
switch (info & XLOG_HEAP_OPMASK) {
case XLOG_HEAP2_MULTI_INSERT:
HeapXlogClearVmBlock(blockvm, bufferinfo);
break;
default:
ereport(PANIC, (errmsg("Heap2RedoVmBlock: unknown op code %u", info)));
}
}
XLogRecParseState *Heap3RedoParseToBlock(XLogReaderState *record, uint32 *blocknum)
{
uint8 info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
XLogRecParseState *recordblockstate = NULL;
*blocknum = 0;
switch (info & XLOG_HEAP_OPMASK) {
case XLOG_HEAP3_NEW_CID:
break;
case XLOG_HEAP3_REWRITE:
break;
case XLOG_HEAP3_INVALID:
recordblockstate = HeapXlogInvalidParseBlock(record, blocknum);
break;
case XLOG_HEAP3_TRUNCATE:
break;
default:
ereport(PANIC, (errmsg("Heap3RedoParseToBlock: unknown op code %u", info)));
}
return recordblockstate;
}
void Heap3RedoDataBlock(XLogBlockHead *blockhead, XLogBlockDataParse *blockdatarec, RedoBufferInfo *bufferinfo)
{
uint8 info = XLogBlockHeadGetInfo(blockhead) & ~XLR_INFO_MASK;
switch (info & XLOG_HEAP_OPMASK) {
case XLOG_HEAP3_NEW_CID:
break;
case XLOG_HEAP3_REWRITE:
break;
case XLOG_HEAP3_INVALID:
HeapXlogInvalidBlock(blockhead, blockdatarec, bufferinfo);
break;
case XLOG_HEAP3_TRUNCATE:
break;
default:
ereport(PANIC, (errmsg("heap3_redo_block: unknown op code %u", info)));
}
}