Files
openGauss-server/src/gausskernel/storage/page/pageparse.cpp

1641 lines
70 KiB
C++

/* ---------------------------------------------------------------------------------------
* *
* 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.
* ---------------------------------------------------------------------------------------
*
* pageparse.cpp
*
* IDENTIFICATION
* src/gausskernel/storage/page/pageparse.cpp
*
* ---------------------------------------------------------------------------------------
*/
#include <stdio.h>
#include "access/htup.h"
#include "access/itup.h"
#include "access/nbtree.h"
#include "access/xlogdefs.h"
#include "commands/tablespace.h"
#include "catalog/catalog.h"
#include "knl/knl_variable.h"
#include "miscadmin.h"
#include "nodes/pg_list.h"
#include "pgstat.h"
#include "postgres.h"
#include "postgres_ext.h"
#include "storage/buf/bufmgr.h"
#include "storage/buf/bufpage.h"
#include "storage/buf/buf_internals.h"
#include "storage/checksum.h"
#include "storage/smgr/relfilenode.h"
#include "storage/smgr/segment.h"
#include "storage/smgr/smgr.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
#include "utils/palloc.h"
#include "utils/relmapper.h"
#include "pageparse.h"
#include "storage/file/fio_device_com.h"
#define INVALID_FD (-1)
typedef enum {
BTREE_INDEX = 0,
UBTREE_INDEX,
INDEX_BOTT
} INDEX_TYPE;
typedef struct UndoHeader {
UndoRecordHeader whdr;
UndoRecordBlock wblk;
UndoRecordTransaction wtxn;
UndoRecordPayload wpay;
UndoRecordOldTd wtd;
UndoRecordPartition wpart;
UndoRecordTablespace wtspc;
StringInfoData rawdata;
} UndoHeader;
void CheckUser(const char *fName)
{
if (!superuser() && !isOperatoradmin(GetUserId()))
ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
(errmsg("Must be system admin or operator admin in operation mode can use %s.", fName))));
}
static void ParseUHeapPageHeader(void *page, BlockNumber blkno, BlockNumber endBlk, char *output);
static void ParseUHeapPageTDInfo(void *page, char *output);
static void ParseUHeapPageItem(Item item, UHeapTuple tuple, char *output);
static void ParseUHeapPageItemUndo(void *page, ItemPointer ctid, char *output);
static void ParseUHeapPageData(void *page, BlockNumber blkno, char *output, bool dumpUndo = false);
static void ParseUHeapPageSpecialInfo(void *page, char *output);
static void ParseUHeapPage(void *page, BlockNumber blkno, BlockNumber endBlk, char *output, bool dumpUndo = false);
static void ParseIndexPageHeader(void *page, int type, BlockNumber blkno, BlockNumber endBlk, char *output);
static void ParseIndexPageItem(Item item, int type, uint32 len, char *output);
static void ParseIndexPageSpecialInfo(void *page, int type, char *output);
static void ParseIndexPageData(void *page, int type, char *output);
static void ParseIndexPage(void *page, int type, BlockNumber blkno, BlockNumber endBlk, char *output);
static void formatBitmap(const unsigned char *start, int len, char bit1, char bit0, char *strOutput)
{
errno_t rc = EOK;
for (int i = 0; i < len; ++i) {
unsigned char ch = start[i];
unsigned char bitmask = 1;
/* print 8 bits within a loop */
do {
rc = snprintf_s(strOutput + (int)strlen(strOutput), MAXOUTPUTLEN, MAXOUTPUTLEN - 1, "%c",
((ch & bitmask) ? bit1 : bit0));
securec_check_ss(rc, "\0", "\0");
bitmask <<= 1;
} while (bitmask != 0);
rc = snprintf_s(strOutput + (int)strlen(strOutput), MAXOUTPUTLEN, MAXOUTPUTLEN - 1, " ");
securec_check_ss(rc, "\0", "\0");
}
}
/* init RelFileNode and outputFilename */
void PrepForRead(char *path, int64 blocknum, char *relation_type, char *outputFilename, RelFileNode *relnode,
bool parse_page)
{
size_t len = strlen(path);
if (len > strlen(COMPRESS_STR) &&
strcmp(path + len - strlen(COMPRESS_STR), COMPRESS_STR) == 0) {
ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
(errmsg("compressed table file is not allowed here."))));
}
char *pathFirstpart = (char *)palloc(MAXFNAMELEN * sizeof(char));
errno_t rc = memset_s(pathFirstpart, MAXFNAMELEN, 0, MAXFNAMELEN);
securec_check(rc, "\0", "\0");
relnode->opt = 0;
if ((strcmp(relation_type, "segment") == 0)) {
relnode->bucketNode = SegmentBktId;
char *bucketNodestr = strstr(path, "_b");
if (NULL != bucketNodestr) {
bucketNodestr += TWO; /* delete first two chars: _b */
relnode->bucketNode = (int2)pg_strtouint64(bucketNodestr, NULL, TENBASE);
rc = strncpy_s(pathFirstpart, strlen(path) - strlen(bucketNodestr) - 1, path,
strlen(path) - strlen(bucketNodestr) - TWO);
securec_check(rc, "\0", "\0");
}
} else {
relnode->bucketNode = InvalidBktId;
}
RelFileNodeForkNum relfilenode;
if (strlen(pathFirstpart) == 0) {
relfilenode = relpath_to_filenode(path);
} else {
relfilenode = relpath_to_filenode(pathFirstpart);
}
if (relfilenode.rnode.node.spcNode == 0)
ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
(errmsg("The tablespace oid is 0. Please check the first parameter path. "
"If you are not sure about the table path, please check pg_relation_filepath."))));
RelFileNodeRelCopy(*relnode, relfilenode.rnode.node);
relnode->opt = 0;
char *pagesuffix = "page";
char *xlogsuffix = "xlog";
rc = snprintf_s(outputFilename + (int)strlen(outputFilename), MAXFILENAME, MAXFILENAME - 1, "%s/%u_%u_%u_%d.%s",
t_thrd.proc_cxt.DataDir, relnode->spcNode, relnode->dbNode, relnode->relNode, blocknum,
(parse_page ? pagesuffix : xlogsuffix));
securec_check_ss(rc, "\0", "\0");
pfree_ext(pathFirstpart);
}
static void ParseHeapHeader(const PageHeader page, char *strOutput, BlockNumber blockNum, BlockNumber block_endpoint)
{
errno_t rc = EOK;
rc = snprintf_s(strOutput + (int)strlen(strOutput), MAXOUTPUTLEN, MAXOUTPUTLEN - 1,
"Page information of block %u/%u", blockNum, block_endpoint);
securec_check_ss(rc, "\0", "\0");
rc = snprintf_s(strOutput + (int)strlen(strOutput), MAXOUTPUTLEN, MAXOUTPUTLEN - 1, "\n\tpd_lsn: %X/%X",
(uint32)(PageGetLSN(page) >> XIDTHIRTYTWO), (uint32)PageGetLSN(page));
securec_check_ss(rc, "\0", "\0");
bool checksum_matched = false;
if (CheckPageZeroCases(page)) {
uint16 checksum = pg_checksum_page((char *)page, (BlockNumber)blockNum);
checksum_matched = (checksum == page->pd_checksum);
}
rc = snprintf_s(strOutput + (int)strlen(strOutput), MAXOUTPUTLEN, MAXOUTPUTLEN - 1,
"\n\tpd_checksum: 0x%X, verify %s", page->pd_checksum, checksum_matched ? "success" : "fail");
securec_check_ss(rc, "\0", "\0");
rc = strcat_s(strOutput, MAXOUTPUTLEN, "\n\tpd_flags: ");
securec_check(rc, "\0", "\0");
if (PageHasFreeLinePointers(page)) {
rc = strcat_s(strOutput, MAXOUTPUTLEN, "PD_HAS_FREE_LINES ");
securec_check(rc, "\0", "\0");
}
if (PageIsFull(page)) {
rc = strcat_s(strOutput, MAXOUTPUTLEN, "PD_PAGE_FULL ");
securec_check(rc, "\0", "\0");
}
if (PageIsAllVisible(page)) {
rc = strcat_s(strOutput, MAXOUTPUTLEN, "PD_ALL_VISIBLE ");
securec_check(rc, "\0", "\0");
}
if (PageIsCompressed(page)) {
rc = strcat_s(strOutput, MAXOUTPUTLEN, "PD_COMPRESSED_PAGE ");
securec_check(rc, "\0", "\0");
}
if (PageIsLogical(page)) {
rc = strcat_s(strOutput, MAXOUTPUTLEN, "PD_LOGICAL_PAGE ");
securec_check(rc, "\0", "\0");
}
if (PageIsEncrypt(page)) {
rc = strcat_s(strOutput, MAXOUTPUTLEN, "PD_ENCRYPT_PAGE ");
securec_check(rc, "\0", "\0");
}
rc = snprintf_s(strOutput + (int)strlen(strOutput), MAXOUTPUTLEN, MAXOUTPUTLEN - 1, "\n\tpd_lower: %u, %s",
page->pd_lower, PageIsEmpty(page) ? "empty" : "non-empty");
securec_check_ss(rc, "\0", "\0");
rc = snprintf_s(strOutput + (int)strlen(strOutput), MAXOUTPUTLEN, MAXOUTPUTLEN - 1, "\n\tpd_upper: %u, %s",
page->pd_upper, PageIsNew(page) ? "new" : "old");
securec_check_ss(rc, "\0", "\0");
rc = snprintf_s(strOutput + (int)strlen(strOutput), MAXOUTPUTLEN, MAXOUTPUTLEN - 1, "\n\tpd_special: %u, size %u",
page->pd_special, PageGetSpecialSize(page));
securec_check_ss(rc, "\0", "\0");
rc = snprintf_s(strOutput + (int)strlen(strOutput), MAXOUTPUTLEN, MAXOUTPUTLEN - 1,
"\n\tPage size & version: %u, %u", (uint16)PageGetPageSize(page), (uint16)PageGetPageLayoutVersion(page));
securec_check_ss(rc, "\0", "\0");
rc = snprintf_s(strOutput + (int)strlen(strOutput), MAXOUTPUTLEN, MAXOUTPUTLEN - 1,
"\n\tpd_xid_base: %lu, pd_multi_base: %lu", ((HeapPageHeader)(page))->pd_xid_base,
((HeapPageHeader)(page))->pd_multi_base);
securec_check_ss(rc, "\0", "\0");
rc = snprintf_s(strOutput + (int)strlen(strOutput), MAXOUTPUTLEN, MAXOUTPUTLEN - 1, "\n\tpd_prune_xid: %lu",
((HeapPageHeader)(page))->pd_prune_xid + ((HeapPageHeader)(page))->pd_xid_base);
securec_check_ss(rc, "\0", "\0");
}
static void PrintInfomask(HeapTupleHeader tup, char *strOutput)
{
errno_t rc = EOK;
if (tup->t_infomask & HEAP_HASNULL) {
rc = strcat_s(strOutput, MAXOUTPUTLEN, "HEAP_HASNULL ");
securec_check(rc, "\0", "\0");
}
if (tup->t_infomask & HEAP_HASVARWIDTH) {
rc = strcat_s(strOutput, MAXOUTPUTLEN, "HEAP_HASVARWIDTH ");
securec_check(rc, "\0", "\0");
}
if (tup->t_infomask & HEAP_HASEXTERNAL) {
rc = strcat_s(strOutput, MAXOUTPUTLEN, "HEAP_HASEXTERNAL ");
securec_check(rc, "\0", "\0");
}
if (tup->t_infomask & HEAP_HASOID) {
rc = snprintf_s(strOutput + (int)strlen(strOutput), MAXOUTPUTLEN, MAXOUTPUTLEN - 1, "HEAP_HASOID(%d) ",
HeapTupleHeaderGetOid(tup));
securec_check_ss(rc, "\0", "\0");
}
if (tup->t_infomask & HEAP_COMPRESSED) {
rc = strcat_s(strOutput, MAXOUTPUTLEN, "HEAP_COMPRESSED ");
securec_check(rc, "\0", "\0");
}
if (tup->t_infomask & HEAP_COMBOCID) {
rc = strcat_s(strOutput, MAXOUTPUTLEN, "HEAP_COMBOCID ");
securec_check(rc, "\0", "\0");
}
if (tup->t_infomask & HEAP_XMAX_EXCL_LOCK) {
rc = strcat_s(strOutput, MAXOUTPUTLEN, "HEAP_XMAX_EXCL_LOCK ");
securec_check(rc, "\0", "\0");
}
if (tup->t_infomask & HEAP_XMAX_SHARED_LOCK) {
rc = strcat_s(strOutput, MAXOUTPUTLEN, "HEAP_XMAX_SHARED_LOCK ");
securec_check(rc, "\0", "\0");
}
if (tup->t_infomask & HEAP_XMIN_COMMITTED) {
rc = strcat_s(strOutput, MAXOUTPUTLEN, "HEAP_XMIN_COMMITTED ");
securec_check(rc, "\0", "\0");
}
if (tup->t_infomask & HEAP_XMIN_INVALID) {
rc = strcat_s(strOutput, MAXOUTPUTLEN, "HEAP_XMIN_INVALID ");
securec_check(rc, "\0", "\0");
}
if (tup->t_infomask & HEAP_XMAX_COMMITTED) {
rc = strcat_s(strOutput, MAXOUTPUTLEN, "HEAP_XMAX_COMMITTED ");
securec_check(rc, "\0", "\0");
}
if (tup->t_infomask & HEAP_XMAX_INVALID) {
rc = strcat_s(strOutput, MAXOUTPUTLEN, "HEAP_XMAX_INVALID ");
securec_check(rc, "\0", "\0");
}
if (tup->t_infomask & HEAP_XMAX_IS_MULTI) {
rc = strcat_s(strOutput, MAXOUTPUTLEN, "HEAP_XMAX_IS_MULTI ");
securec_check(rc, "\0", "\0");
}
if (tup->t_infomask & HEAP_UPDATED) {
rc = strcat_s(strOutput, MAXOUTPUTLEN, "HEAP_UPDATED ");
securec_check(rc, "\0", "\0");
}
if ((tup->t_infomask & HEAP_HAS_8BYTE_UID)) {
rc = strcat_s(strOutput, MAXOUTPUTLEN, "HEAP_HAS_8BYTE_UID ");
securec_check(rc, "\0", "\0");
} else {
rc = strcat_s(strOutput, MAXOUTPUTLEN, "HEAP_HAS_NO_UID ");
securec_check(rc, "\0", "\0");
}
rc = strcat_s(strOutput, MAXOUTPUTLEN, "\n\t\t\tt_infomask2: ");
securec_check(rc, "\0", "\0");
if (tup->t_infomask2 & HEAP_HOT_UPDATED) {
rc = strcat_s(strOutput, MAXOUTPUTLEN, "HEAP_HOT_UPDATED ");
securec_check(rc, "\0", "\0");
}
if (tup->t_infomask2 & HEAP_ONLY_TUPLE) {
rc = strcat_s(strOutput, MAXOUTPUTLEN, "HEAP_ONLY_TUPLE ");
securec_check(rc, "\0", "\0");
}
}
static void ParseTupleHeader(const PageHeader page, uint lineno, char *strOutput)
{
errno_t rc = EOK;
ItemId lp = PageGetItemId(page, lineno);
rc = snprintf_s(strOutput + (int)strlen(strOutput), MAXOUTPUTLEN, MAXOUTPUTLEN - 1,
"\n\n\t\tTuple #%u is normal: length %u, offset %u", lineno, ItemIdGetLength(lp), ItemIdGetOffset(lp));
securec_check_ss(rc, "\0", "\0");
HeapTupleData dummyTuple;
HeapTupleHeader tup = (HeapTupleHeader)(PageGetItem(page, lp));
dummyTuple.t_data = tup;
dummyTuple.t_xid_base = ((HeapPageHeader)(page))->pd_xid_base;
dummyTuple.t_multi_base = ((HeapPageHeader)(page))->pd_multi_base;
rc = snprintf_s(strOutput + (int)strlen(strOutput), MAXOUTPUTLEN, MAXOUTPUTLEN - 1,
"\n\t\t\tt_xmin/t_xmax/t_cid: %lu/%lu/%u", HeapTupleGetRawXmin(&dummyTuple), HeapTupleGetRawXmax(&dummyTuple),
HeapTupleHeaderGetRawCommandId(tup));
securec_check_ss(rc, "\0", "\0");
rc = snprintf_s(strOutput + (int)strlen(strOutput), MAXOUTPUTLEN, MAXOUTPUTLEN - 1,
"\n\t\t\tctid:(block %u/%u, offset %u)", tup->t_ctid.ip_blkid.bi_hi, tup->t_ctid.ip_blkid.bi_lo,
tup->t_ctid.ip_posid);
securec_check_ss(rc, "\0", "\0");
rc = strcat_s(strOutput, MAXOUTPUTLEN, "\n\t\t\tt_infomask: ");
securec_check(rc, "\0", "\0");
PrintInfomask(tup, strOutput);
rc = snprintf_s(strOutput + (int)strlen(strOutput), MAXOUTPUTLEN, MAXOUTPUTLEN - 1, "Attrs Num: %d",
HeapTupleHeaderGetNatts(tup, NULL));
securec_check_ss(rc, "\0", "\0");
rc = snprintf_s(strOutput + (int)strlen(strOutput), MAXOUTPUTLEN, MAXOUTPUTLEN - 1, "\n\t\t\tt_hoff: %u",
tup->t_hoff);
securec_check_ss(rc, "\0", "\0");
rc = strcat_s(strOutput, MAXOUTPUTLEN, "\n\t\t\tt_bits: ");
securec_check(rc, "\0", "\0");
formatBitmap((const unsigned char *)tup->t_bits, BITMAPLEN(HeapTupleHeaderGetNatts(tup, NULL)), 'V', 'N',
strOutput);
}
static void ParseHeapPage(const PageHeader page, BlockNumber blockNum, char *strOutput, BlockNumber block_endpoint)
{
errno_t rc = EOK;
if (PageIsNew(page)) {
rc = snprintf_s(strOutput + (int)strlen(strOutput), MAXOUTPUTLEN, MAXOUTPUTLEN - 1,
"Page information of block %u/%u : new page\n", blockNum, block_endpoint);
securec_check_ss(rc, "\0", "\0");
ParseHeapHeader(page, strOutput, blockNum, block_endpoint);
rc = snprintf_s(strOutput + (int)strlen(strOutput), MAXOUTPUTLEN, MAXOUTPUTLEN - 1, "\n");
securec_check_ss(rc, "\0", "\0");
return;
}
if (page->pd_lower < GetPageHeaderSize(page) || page->pd_lower > page->pd_upper ||
page->pd_upper > page->pd_special || page->pd_special > BLCKSZ ||
page->pd_special != MAXALIGN(page->pd_special)) {
rc = snprintf_s(strOutput + (int)strlen(strOutput),
MAXOUTPUTLEN,
MAXOUTPUTLEN - 1,
"The page data is corrupted, corrupted page pointers: lower = %u, upper = %u, special = %u\n",
page->pd_lower,
page->pd_upper,
page->pd_special);
securec_check_ss(rc, "\0", "\0");
return;
}
ParseHeapHeader(page, strOutput, blockNum, block_endpoint);
/* parse tuple header */
rc = strcat_s(strOutput, MAXOUTPUTLEN, "\n\n\tHeap tuple information on this page");
securec_check(rc, "\0", "\0");
uint nline = PageGetMaxOffsetNumber((Page)page);
uint nunused = 0, nnormal = 0, ndead = 0;
for (uint i = (OffsetNumber)1; i <= nline; i++) {
ItemId lp = PageGetItemId(page, i);
if (ItemIdIsNormal(lp)) {
nnormal++;
ParseTupleHeader(page, i, strOutput);
} else if (ItemIdIsDead(lp)) {
ndead++;
rc = snprintf_s(strOutput + (int)strlen(strOutput), MAXOUTPUTLEN, MAXOUTPUTLEN - 1,
"\n\n\t\tTuple #%u is dead: length %u, offset %u", i, ItemIdGetLength(lp), ItemIdGetOffset(lp));
securec_check_ss(rc, "\0", "\0");
} else {
nunused++;
rc = snprintf_s(strOutput + (int)strlen(strOutput), MAXOUTPUTLEN, MAXOUTPUTLEN - 1,
"\n\n\t\tTuple #%u is nunused: ", i);
securec_check_ss(rc, "\0", "\0");
}
}
rc = snprintf_s(strOutput + (int)strlen(strOutput), MAXOUTPUTLEN, MAXOUTPUTLEN - 1,
"\n\tSummary (%u total): %u normal, %u unused, %u dead\n\nNormal Heap Page, special space is %u\n\n", nline,
nnormal, nunused, ndead, (uint)PageGetSpecialSize(page));
securec_check_ss(rc, "\0", "\0");
}
static void ParseOnePage(const PageHeader page, BlockNumber blockNum, char *strOutput, char *relation_type,
BlockNumber block_endpoint, bool dumpUndo)
{
errno_t rc = EOK;
if (strcmp(relation_type, "heap") == 0) {
if ((PG_HEAP_PAGE_LAYOUT_VERSION != (uint16)PageGetPageLayoutVersion(page) &&
(uint16)PageGetPageLayoutVersion(page) != 0) || PageGetSpecialSize(page) != 0) {
ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
(errmsg("The target page is not heap, the given page version is: %u",
(uint16)PageGetPageLayoutVersion(page)))));
}
ParseHeapPage(page, blockNum, strOutput, block_endpoint);
} else if (strcmp(relation_type, "uheap") == 0) {
if (PG_UHEAP_PAGE_LAYOUT_VERSION != (uint16)PageGetPageLayoutVersion(page) || PageGetSpecialSize(page) != 0) {
ereport(LOG,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmodule(MOD_USTORE),
errmsg("The target page(%u) is not uheap, the given page version is: %u",
blockNum, (uint16)PageGetPageLayoutVersion(page)),
errdetail("N/A"),
errcause("Page version is not uheap, or page is new."),
erraction("Check input parameters or perform checkpoint")));
rc = snprintf_s(strOutput + (int)strlen(strOutput), MAXOUTPUTLEN, MAXOUTPUTLEN,
"Page (%u) is not uheap ,or page is new.\n", blockNum);
securec_check_ss(rc, "\0", "\0");
return;
}
ParseUHeapPage((void *)page, blockNum, block_endpoint, strOutput, dumpUndo);
} else if (strcmp(relation_type, "btree") == 0) {
if ((PG_COMM_PAGE_LAYOUT_VERSION != (uint16)PageGetPageLayoutVersion(page) &&
(uint16)PageGetPageLayoutVersion(page) != 0) || PageGetSpecialSize(page) == 0) {
ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
(errmsg("The target page is not btree, the given page version is: %u",
(uint16)PageGetPageLayoutVersion(page)))));
}
ParseIndexPage((void *)page, BTREE_INDEX, blockNum, block_endpoint, strOutput);
} else if (strcmp(relation_type, "ubtree") == 0) {
if ((PG_COMM_PAGE_LAYOUT_VERSION != (uint16)PageGetPageLayoutVersion(page) &&
(uint16)PageGetPageLayoutVersion(page) != 0) || PageGetSpecialSize(page) == 0) {
ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
(errmsg("The target page is not ubtree, the given page version is: %u",
(uint16)PageGetPageLayoutVersion(page)))));
}
ParseIndexPage((void *)page, UBTREE_INDEX, blockNum, block_endpoint, strOutput);
} else if (strcmp(relation_type, "segment") == 0) {
rc = strcat_s(strOutput, MAXOUTPUTLEN, "Parse segment table.");
securec_check(rc, "\0", "\0");
ParseHeapPage(page, blockNum, strOutput, block_endpoint);
} else {
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_PARAMETER), (errmsg("Only support heap, uheap, btree, ubtree, segment."))));
}
}
static void check_rdStatus(SMGR_READ_STATUS rdStatus, BlockNumber blockNum, const PageHeader page, char *strOutput1)
{
errno_t rc = EOK;
if (rdStatus == SMGR_RD_CRC_ERROR) {
uint16 checksum = pg_checksum_page((char *)page, blockNum);
rc = snprintf_s(strOutput1, SHORTOUTPUTLEN, SHORTOUTPUTLEN - 1,
"\nFor page %u, page verification failed, calculated checksum 0x%X but expected 0x%X.\n",
blockNum, checksum, page->pd_checksum);
securec_check_ss(rc, "\0", "\0");
} else if (rdStatus == SMGR_RD_NO_BLOCK) {
rc = snprintf_s(strOutput1, SHORTOUTPUTLEN, SHORTOUTPUTLEN - 1, "\tThe page %u does not exist.\n", blockNum);
securec_check_ss(rc, "\0", "\0");
}
}
static bool readFromMemory(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum, char *relation_type,
char *strOutput, FILE *outputfile, char *outputFilename, bool dumpUndo)
{
errno_t rc = EOK;
SegPageLocation loc;
loc.blocknum = 0;
/* for segment, update to physical blocknum */
if (strcmp(relation_type, "segment") == 0) {
loc = seg_get_physical_location(smgr->smgr_rnode.node, forkNum, blockNum);
}
BufferTag new_tag;
INIT_BUFFERTAG(new_tag, smgr->smgr_rnode.node, forkNum, blockNum);
uint32 new_hash = BufTableHashCode(&new_tag);
LWLock *partition_lock = BufMappingPartitionLock(new_hash);
ResourceOwnerEnlargeBuffers(t_thrd.utils_cxt.CurrentResourceOwner);
/* LW lock to avoid concurrent read/write */
(void)LWLockAcquire(partition_lock, LW_SHARED);
int buf_id = BufTableLookup(&new_tag, new_hash);
do {
if (buf_id >= 0) { /* read from memory */
BufferDesc *bufDesc = GetBufferDescriptor(buf_id);
bool valid = (strcmp(relation_type, "segment") == 0) ? SegPinBuffer(bufDesc) : PinBuffer(bufDesc, NULL);
if (!valid)
break; /* pin failed, read disk */
LWLockRelease(partition_lock);
(void)LWLockAcquire(BufferDescriptorGetContentLock(bufDesc), LW_SHARED); /* acquire content_lock */
Buffer buf = BufferDescriptorGetBuffer(bufDesc);
const PageHeader page = (const PageHeader)BufferGetPage(buf);
rc = strcat_s(strOutput, MAXOUTPUTLEN, "The target page is from memory. ");
securec_check(rc, "\0", "\0");
if (strcmp(relation_type, "segment") == 0) {
ParseOnePage(page, loc.blocknum, strOutput, relation_type, loc.blocknum, false); /* physical blocknum */
LWLockRelease(BufferDescriptorGetContentLock(bufDesc)); /* release content_lock */
SegUnpinBuffer(bufDesc);
} else {
ParseOnePage(page, blockNum, strOutput, relation_type, blockNum, dumpUndo);
LWLockRelease(BufferDescriptorGetContentLock(bufDesc)); /* release content_lock */
UnpinBuffer(bufDesc, true);
}
CheckWriteFile(outputfile, outputFilename, strOutput);
pfree_ext(strOutput);
return true;
}
} while (0);
LWLockRelease(partition_lock);
return false;
}
static void CheckSegment(RelFileNode *relnode, ForkNumber forkNum, FILE *outputfile, char *outputFilename)
{
SegSpace *spc = spc_open(relnode->spcNode, relnode->dbNode, false);
if (spc == NULL || !spc_datafile_exist(spc, 1, forkNum)) {
CheckCloseFile(outputfile, outputFilename, false);
ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), (errmsg("Page doesn't exist."))));
}
BlockNumber size = spc_size(spc, 1, forkNum);
if (relnode->relNode >= size) {
CheckCloseFile(outputfile, outputFilename, false);
ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
(errmsg("The target page %u doesn't exist, the current max is %u.", relnode->relNode, size - 1))));
}
RelFileNode *relnodeHead = (RelFileNode *) palloc(sizeof(RelFileNode));
relnodeHead->spcNode = relnode->spcNode;
relnodeHead->dbNode = relnode->dbNode;
relnodeHead->relNode = 1;
relnodeHead->opt = relnode->opt;
relnodeHead->bucketNode = relnode->bucketNode;
relnodeHead->opt = relnode->opt;
Buffer buffer_temp = ReadBufferFast(spc, *relnodeHead, forkNum, relnode->relNode, RBM_NORMAL);
if (!BufferIsValid(buffer_temp)) {
CheckCloseFile(outputfile, outputFilename, false);
ereport(ERROR, (errcode_for_file_access(), errmsg("Segment Head is invalid %u/%u/%u %d %u",
relnodeHead->spcNode, relnodeHead->dbNode, relnodeHead->relNode, forkNum, relnode->relNode)));
}
pfree_ext(relnodeHead);
SegmentHead *head = (SegmentHead *)PageGetContents(BufferGetPage(buffer_temp));
SegReleaseBuffer(buffer_temp);
if (!(IsNormalSegmentHead(head) && relnode->bucketNode == SegmentBktId) &&
!(IsBucketMainHead(head) && IsBucketFileNode(*relnode))) {
CheckCloseFile(outputfile, outputFilename, false);
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
(errmsg("Page header does not match with corresponding segment type."))));
}
}
void ValidateParameterPath(RelFileNode rnode, char *str)
{
char *path = relpathbackend(rnode, InvalidBackendId, MAIN_FORKNUM);
if ((strcmp(path, str) != 0))
ereport(ERROR, (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), errmsg("\"%s\" is invalid input", str)));
}
char *ParsePage(char *path, int64 blocknum, char *relation_type, bool read_memory, bool dumpUndo)
{
errno_t rc = EOK;
/* check parameters */
if (blocknum > MaxBlockNumber || blocknum < -1)
ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
(errmsg("Blocknum should be between -1 and %u.", MaxBlockNumber))));
/* initialize */
char *strOutput = (char *)palloc(MAXOUTPUTLEN * sizeof(char));
rc = memset_s(strOutput, MAXOUTPUTLEN, 0, MAXOUTPUTLEN);
securec_check(rc, "\0", "\0");
RelFileNode *relnode = (RelFileNode *)palloc(sizeof(RelFileNode));
char *outputFilename = (char *)palloc(MAXFILENAME * sizeof(char));
rc = memset_s(outputFilename, MAXFILENAME, 0, MAXFILENAME);
securec_check(rc, "\0", "\0");
PrepForRead(path, blocknum, relation_type, outputFilename, relnode, true);
ValidateParameterPath(*relnode, path);
FILE *outputfile = fopen(outputFilename, "w");
CheckOpenFile(outputfile, outputFilename);
ForkNumber forkNum = MAIN_FORKNUM;
SMgrRelation smgr = smgropen(*relnode, InvalidBackendId, GetColumnNum(forkNum));
if (strcmp(relation_type, "segment") == 0)
CheckSegment(relnode, forkNum, outputfile, outputFilename);
BlockNumber maxBlockNum = smgrnblocks(smgr, forkNum);
if ((blocknum != -1 && blocknum > maxBlockNum) || (maxBlockNum == 0)) {
CheckCloseFile(outputfile, outputFilename, false);
ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), (errmsg(
"The max blocknum of current file is %u. Target blocknum exceeds current file size.", maxBlockNum))));
}
pfree_ext(relnode);
BlockNumber blockNum = 0;
BlockNumber block_endpoint = 0;
/* only parse one block, check if it is in memory */
if (read_memory) {
blockNum = (BlockNumber)blocknum;
if (readFromMemory(smgr, forkNum, blockNum, relation_type, strOutput, outputfile, outputFilename, dumpUndo)) {
/* found in memory */
CheckCloseFile(outputfile, outputFilename, true);
smgrclose(smgr);
return outputFilename;
}
}
/* read from disk */
rc = strcat_s(strOutput, MAXOUTPUTLEN, "The target page is from disk. ");
securec_check(rc, "\0", "\0");
CheckWriteFile(outputfile, outputFilename, strOutput);
pfree_ext(strOutput);
if (blocknum >= 0) { /* only parse one block */
blockNum = blocknum;
block_endpoint = blocknum;
} else { /* blocknum == -1, parse all blocks */
block_endpoint = maxBlockNum + 1;
}
/* if not declare a single block, then loop all blocks */
while (blockNum <= block_endpoint) {
CHECK_FOR_INTERRUPTS(); /* Allow cancel/die interrupts */
char *strOutput = (char *)palloc(MAXOUTPUTLEN * sizeof(char));
rc = memset_s(strOutput, MAXOUTPUTLEN, 0, MAXOUTPUTLEN);
securec_check(rc, "\0", "\0");
char *buffer = (char *)palloc0(BLCKSZ);
SMGR_READ_STATUS rdStatus = smgrread(smgr, forkNum, blockNum, buffer);
const PageHeader page = (const PageHeader)buffer;
char *strOutput1 = (char *)palloc(MAXOUTPUTLEN * sizeof(char));
rc = memset_s(strOutput1, SHORTOUTPUTLEN, 0, SHORTOUTPUTLEN);
securec_check(rc, "\0", "\0");
check_rdStatus(rdStatus, blockNum, page, strOutput1);
CheckWriteFile(outputfile, outputFilename, strOutput1);
pfree_ext(strOutput1);
if (strcmp(relation_type, "segment") == 0) {
SegPageLocation loc = seg_get_physical_location(smgr->smgr_rnode.node, forkNum, blockNum);
SegPageLocation endloc = seg_get_physical_location(smgr->smgr_rnode.node, forkNum, block_endpoint);
ParseOnePage(page, loc.blocknum, strOutput, relation_type, endloc.blocknum, false); /* physical blocknum */
} else
ParseOnePage(page, blockNum, strOutput, relation_type, block_endpoint, dumpUndo);
CheckWriteFile(outputfile, outputFilename, strOutput);
pfree_ext(strOutput);
pfree_ext(buffer);
blockNum++;
}
CheckCloseFile(outputfile, outputFilename, true);
smgrclose(smgr);
return outputFilename;
}
static void ParseUHeapPageHeader(void *page, BlockNumber blkno, BlockNumber endBlk, char *output)
{
errno_t rc = EOK;
bool chksumResult = false;
UHeapPageHeader pageHeader = NULL;
if (page == NULL || output == NULL) {
return;
}
pageHeader = (UHeapPageHeader)page;
rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "Page information of block %u/%u\n",
blkno, endBlk);
securec_check_ss(rc, "\0", "\0");
rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "\tpd_lsn: %X/%X\n",
(uint32)(PageGetLSN(pageHeader) >> XIDTHIRTYTWO), (uint32)PageGetLSN(pageHeader));
securec_check_ss(rc, "\0", "\0");
if (CheckPageZeroCases((PageHeader)pageHeader)) {
uint16 checksum = pg_checksum_page((char *)pageHeader, (BlockNumber)blkno);
chksumResult = (checksum == pageHeader->pd_checksum);
}
rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "\tpd_checksum: 0x%X, verify %s\n",
pageHeader->pd_checksum, chksumResult ? "success" : "fail");
securec_check_ss(rc, "\0", "\0");
if (UPageHasFreeLinePointers(pageHeader)) {
rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "PAGE_HAS_FREE_LINES.");
securec_check_ss(rc, "\0", "\0");
}
if (UPageIsFull(pageHeader)) {
rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "PAGE_FULL.");
securec_check_ss(rc, "\0", "\0");
}
rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN,
"\tpd_lower: %u, %s\n\tpd_upper: %u, %s\n\tpd_special: %u, size %u\n", pageHeader->pd_lower,
PageIsEmpty(pageHeader) ? "empty" : "non-empty", pageHeader->pd_upper,
PageIsNew(pageHeader) ? "new page" : "old page", pageHeader->pd_special, PageGetSpecialSize(pageHeader));
securec_check_ss(rc, "\0", "\0");
rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "\tPage size & version: %u, %u\n",
(uint16)PageGetPageSize(pageHeader), (uint16)PageGetPageLayoutVersion(pageHeader));
securec_check_ss(rc, "\0", "\0");
rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "\tpotential_freespace: %u\n",
pageHeader->potential_freespace);
securec_check_ss(rc, "\0", "\0");
rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "\ttd_count: %u\n", pageHeader->td_count);
securec_check_ss(rc, "\0", "\0");
rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "\tpd_prune_xid: %lu\n",
pageHeader->pd_prune_xid);
securec_check_ss(rc, "\0", "\0");
rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN,
"\tpd_xid_base: %lu, pd_multi_base: %lu\n", pageHeader->pd_xid_base, pageHeader->pd_multi_base);
securec_check_ss(rc, "\0", "\0");
return;
}
static void ParseUHeapPageTDInfo(void *page, char *output)
{
TD *tdInfo = NULL;
uint16 tdCount = 0;
UHeapPageTDData *tdPtr = NULL;
UHeapPageHeader pageHeader = NULL;
errno_t rc = EOK;
if (page == NULL || output == NULL) {
return;
}
pageHeader = (UHeapPageHeader)page;
tdPtr = (UHeapPageTDData *)PageGetTDPointer(page);
tdCount = pageHeader->td_count;
rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN,
"\n\n\tUHeap Page TD information, nTDSlots = %u\n", tdCount);
securec_check_ss(rc, "\0", "\0");
for (uint16 i = 0; i < tdCount; i++) {
tdInfo = &(tdPtr->td_info[i]);
rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN,
"\n\t\t TD Slot #%d, xid:%lu, urp:%lu\n", i + 1, tdInfo->xactid, tdInfo->undo_record_ptr);
securec_check_ss(rc, "\0", "\0");
}
return;
}
static void ParseUHeapPageItem(Item item, UHeapTuple tuple, char *output)
{
if (item == NULL || tuple == NULL || output == NULL) {
return;
}
UHeapDiskTuple diskTuple = (UHeapDiskTuple)item;
errno_t rc = EOK;
tuple->disk_tuple = diskTuple;
rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "\t\t\txid:%lu, td:%d, locker_td %d\n",
UHeapTupleGetRawXid(tuple), diskTuple->td_id, diskTuple->reserved);
securec_check_ss(rc, "\0", "\0");
rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "\t\t\tFlag:%u", diskTuple->flag);
securec_check_ss(rc, "\0", "\0");
if (diskTuple->flag & UHEAP_HAS_NULL) {
rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "\n\t\t\tUHEAP_HASNULL");
securec_check_ss(rc, "\0", "\0");
}
if (diskTuple->flag & UHEAP_DELETED) {
rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "\n\t\t\tUHEAP_DELETED\n");
securec_check_ss(rc, "\0", "\0");
}
if (diskTuple->flag & UHEAP_INPLACE_UPDATED) {
rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "\n\t\t\tUHEAP_INPLACE_UPDATED ");
securec_check_ss(rc, "\0", "\0");
}
if (diskTuple->flag & UHEAP_UPDATED) {
rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "\n\t\t\tUHEAP_UPDATED ");
securec_check_ss(rc, "\0", "\0");
}
if (diskTuple->flag & UHEAP_XID_KEYSHR_LOCK) {
rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "\n\t\t\tUHEAP_XID_KEYSHR_LOCK ");
securec_check_ss(rc, "\0", "\0");
}
if (diskTuple->flag & UHEAP_XID_NOKEY_EXCL_LOCK) {
rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "\n\t\t\tUHEAP_XID_NOKEY_EXCL_LOCK ");
securec_check_ss(rc, "\0", "\0");
}
if (diskTuple->flag & UHEAP_XID_EXCL_LOCK) {
rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "\n\t\t\tUHEAP_XID_EXCL_LOCK ");
securec_check_ss(rc, "\0", "\0");
}
if (diskTuple->flag & UHEAP_MULTI_LOCKERS) {
rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "\n\t\t\tUHEAP_MULTI_LOCKERS ");
securec_check_ss(rc, "\0", "\0");
}
if (diskTuple->flag & UHEAP_INVALID_XACT_SLOT) {
rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "\n\t\t\tUHEAP_INVALID_XACT_SLOT ");
securec_check_ss(rc, "\0", "\0");
}
if (diskTuple->flag & SINGLE_LOCKER_XID_IS_LOCK) {
rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "\n\t\t\tSINGLE_LOCKER_XID_IS_LOCK ");
securec_check_ss(rc, "\0", "\0");
}
if (diskTuple->flag & SINGLE_LOCKER_XID_IS_SUBXACT) {
rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN,
"\n\t\t\tSINGLE_LOCKER_XID_IS_SUBXACT ");
securec_check_ss(rc, "\0", "\0");
}
rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "\n\t\t\tFlag2:");
securec_check_ss(rc, "\0", "\0");
rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "\n\t\t\tNumber of columns:: %d\n",
UHeapTupleHeaderGetNatts(diskTuple));
securec_check_ss(rc, "\0", "\0");
rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "\t\t\tHoff: %u\n", diskTuple->t_hoff);
securec_check_ss(rc, "\0", "\0");
return;
}
static int OpenUndoSeg(int zoneId, BlockNumber blkNo)
{
char fileName[MAXUNDOFILENAMELEN] = {0};
errno_t rc = EOK;
int segno = blkNo / UNDOSEG_SIZE;
DECLARE_NODE_COUNT();
GET_UPERSISTENCE_BY_ZONEID(zoneId, nodeCount);
const char *persistence = (upersistence == UNDO_PERMANENT) ? "permanent" : ((upersistence == UNDO_TEMP) ? "temp" :
"unlogged");
rc = snprintf_s(fileName, sizeof(fileName), sizeof(fileName), "undo/%s/%05X.%07zX", persistence, zoneId, segno);
securec_check_ss(rc, "\0", "\0");
int fd = open(fileName, O_RDONLY | PG_BINARY, S_IRUSR | S_IWUSR);
if (fd < 0) {
return INVALID_FD;
}
return fd;
}
bool DumpUndoRecordsBytes(char *destptr, int destlen, char **readeptr, char *endptr, int *myBytesRead, int *alreadyRead)
{
if (*myBytesRead >= destlen) {
*myBytesRead -= destlen;
return true;
}
int remaining = destlen - *myBytesRead;
int maxReadOnCurrPage = endptr - *readeptr;
int canRead = Min(remaining, maxReadOnCurrPage);
if (canRead == 0) {
return false;
}
errno_t rc = memcpy_s(destptr + *myBytesRead, remaining, *readeptr, canRead);
securec_check(rc, "\0", "\0");
*readeptr += canRead;
*alreadyRead += canRead;
*myBytesRead = 0;
return (canRead == remaining);
}
bool ReadUndoRecordByUrp(UndoHeader *urec, char *buffer, int startingByte, int *alreadyRead)
{
char *readptr = buffer + startingByte;
char *endptr = buffer + BLCKSZ;
int myBytesRead = *alreadyRead;
if (!DumpUndoRecordsBytes((char*)&(urec->whdr), SIZE_OF_UNDO_RECORD_HEADER,
&readptr, endptr, &myBytesRead, alreadyRead)) {
return false;
}
if (!DumpUndoRecordsBytes((char*)&(urec->wblk), SIZE_OF_UNDO_RECORD_BLOCK,
&readptr, endptr, &myBytesRead, alreadyRead)) {
return false;
}
if ((urec->whdr.uinfo & UNDO_UREC_INFO_TRANSAC) != 0) {
if (!DumpUndoRecordsBytes((char *)&urec->wtxn, SIZE_OF_UNDO_RECORD_TRANSACTION,
&readptr, endptr, &myBytesRead, alreadyRead)) {
return false;
}
}
if ((urec->whdr.uinfo & UNDO_UREC_INFO_OLDTD) != 0) {
if (!DumpUndoRecordsBytes((char *)&urec->wtd, SIZE_OF_UNDO_RECORD_OLDTD,
&readptr, endptr, &myBytesRead, alreadyRead)) {
return false;
}
}
if ((urec->whdr.uinfo & UNDO_UREC_INFO_HAS_PARTOID) != 0) {
if (!DumpUndoRecordsBytes((char *)&urec->wpart, SIZE_OF_UNDO_RECORD_PARTITION,
&readptr, endptr, &myBytesRead, alreadyRead)) {
return false;
}
}
if ((urec->whdr.uinfo & UNDO_UREC_INFO_HAS_TABLESPACEOID) != 0) {
if (!DumpUndoRecordsBytes((char *)&urec->wtspc, SIZE_OF_UNDO_RECORD_TABLESPACE,
&readptr, endptr, &myBytesRead, alreadyRead)) {
return false;
}
}
if ((urec->whdr.uinfo & UNDO_UREC_INFO_PAYLOAD) != 0) {
if (!DumpUndoRecordsBytes((char *)&urec->wpay, SIZE_OF_UNDO_RECORD_PAYLOAD,
&readptr, endptr, &myBytesRead, alreadyRead)) {
return false;
}
urec->rawdata.len = urec->wpay.payloadlen;
if (urec->rawdata.len > 0) {
if (urec->rawdata.data == NULL) {
urec->rawdata.data = (char *)malloc(urec->rawdata.len);
if (NULL == urec->rawdata.data) {
ereport(WARNING,
(errcode(ERRCODE_OUT_OF_MEMORY),
errmodule(MOD_USTORE),
errmsg("Alloc memory failed."),
errdetail("N/A"),
errcause("Out of memory"),
erraction("N/A")));
return false;
}
}
if (!DumpUndoRecordsBytes((char *)urec->rawdata.data, urec->rawdata.len,
&readptr, endptr, &myBytesRead, alreadyRead)) {
return false;
}
}
}
return true;
}
static bool DumpUndoRecod(UndoRecord *urec)
{
if (urec == NULL) {
return false;
}
bool found = false;
char undoBuf[BLCKSZ] = {'\0'};
off_t seekpos;
int zoneId = UNDO_PTR_GET_ZONE_ID(urec->Urp());
BlockNumber blockNo = UNDO_PTR_GET_BLOCK_NUM(urec->Urp());
uint32 startBytes = UNDO_PTR_GET_PAGE_OFFSET(urec->Urp());
int alreadyRead = 0;
uint32 ret = 0;
int fd = INVALID_FD;
errno_t rc = EOK;
UndoHeader *urecInfo = NULL;
urecInfo = (UndoHeader *)palloc0(sizeof(UndoHeader));
do {
fd = OpenUndoSeg(zoneId, blockNo);
if (fd == INVALID_FD) {
pfree(urecInfo);
return false;
}
seekpos = (off_t)BLCKSZ * (blockNo % ((BlockNumber)UNDOSEG_SIZE));
lseek(fd, seekpos, SEEK_SET);
rc = memset_s(undoBuf, BLCKSZ, 0, BLCKSZ);
securec_check(rc, "\0", "\0");
ret = read(fd, (char *)undoBuf, BLCKSZ);
if (ret != BLCKSZ) {
ereport(WARNING,
(errcode(ERRCODE_OPERATE_FAILED),
errmodule(MOD_USTORE),
errmsg("Read undo with urp(%lu) failed, errno(%d).", urec->Urp(), errno),
errdetail("N/A"),
errcause("Read size is not a block size."),
erraction("N/A")));
free(urecInfo->rawdata.data);
pfree(urecInfo);
close(fd);
return false;
}
if (ReadUndoRecordByUrp(urecInfo, undoBuf, startBytes, &alreadyRead)) {
found = true;
/* Set undorecord. */
urec->SetXid(urecInfo->whdr.xid);
urec->SetCid(urecInfo->whdr.cid);
urec->SetReloid(urecInfo->whdr.reloid);
urec->SetRelfilenode(urecInfo->whdr.relfilenode);
urec->SetUtype(urecInfo->whdr.utype);
urec->SetUinfo(urecInfo->whdr.uinfo);
urec->SetBlkprev(urecInfo->wblk.blkprev);
urec->SetBlkno(urecInfo->wblk.blkno);
urec->SetOffset(urecInfo->wblk.offset);
urec->SetPrevurp(urecInfo->wtxn.prevurp);
urec->SetOldXactId(urecInfo->wtd.oldxactid);
urec->SetPartitionoid(urecInfo->wpart.partitionoid);
urec->SetTablespace(urecInfo->wtspc.tablespace);
urec->SetPayLoadLen(urecInfo->wpay.payloadlen);
if (urecInfo->wpay.payloadlen) {
initStringInfo(urec->Rawdata());
appendBinaryStringInfo(urec->Rawdata(), urecInfo->rawdata.data, urecInfo->wpay.payloadlen);
}
break;
}
startBytes = UNDO_LOG_BLOCK_HEADER_SIZE;
blockNo++;
close(fd);
CHECK_FOR_INTERRUPTS();
} while (true);
if (urecInfo->rawdata.data != NULL) {
free(urecInfo->rawdata.data);
}
pfree(urecInfo);
close(fd);
return found;
}
static void DumpUndoRecordByUrpFromPage(void *page, ItemPointer ctid, UndoRecPtr urp, int tdSlot,
UHeapDiskTuple diskTuple, char *output)
{
errno_t rc = EOK;
int prevTdSlot = tdSlot;
int currentTdSlot = tdSlot;
UndoRecPtr currUrp = urp;
fetch_undo:
VerifyMemoryContext();
UndoRecord *urec = New(CurrentMemoryContext)UndoRecord();
urec->SetUrp(currUrp);
bool fetch = DumpUndoRecod(urec);
if (fetch) {
Assert(urec != NULL);
if (urec->Utype() == UNDO_INSERT || urec->Utype() == UNDO_MULTI_INSERT) {
diskTuple->flag &= ~UHEAP_VIS_STATUS_MASK;
diskTuple->xid = (ShortTransactionId)FrozenTransactionId;
} else if (urec->Utype() == UNDO_INPLACE_UPDATE) {
Assert(urec->Rawdata() != NULL);
Assert(urec->Rawdata()->len >= (int)SizeOfUHeapDiskTupleData);
rc = memcpy_s((char *)diskTuple + OffsetTdId, SizeOfUHeapDiskTupleHeaderExceptXid,
urec->Rawdata()->data + sizeof(uint8), SizeOfUHeapDiskTupleHeaderExceptXid);
securec_check(rc, "", "");
diskTuple->xid = (ShortTransactionId)FrozenTransactionId;
} else {
Assert(urec->Rawdata() != NULL);
Assert(urec->Rawdata()->len >= (int)SizeOfUHeapDiskTupleHeaderExceptXid);
rc = memcpy_s(((char *)diskTuple + OffsetTdId), SizeOfUHeapDiskTupleHeaderExceptXid,
urec->Rawdata()->data, SizeOfUHeapDiskTupleHeaderExceptXid);
securec_check(rc, "", "");
diskTuple->xid = (ShortTransactionId)FrozenTransactionId;
}
currentTdSlot = diskTuple->td_id;
/* Find matched undo version, and output. */
if (ItemPointerGetBlockNumber(ctid) == urec->Blkno() && urec->Offset() == ctid->ip_posid) {
rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN,
"\n\t\tUndoRecPtr(%lu): whdr = xid(%lu), cid(%u), reloid(%u), relfilenode(%u), utype(%u), uinfo(%u).\n",
urec->Urp(), urec->Xid(), urec->Cid(), urec->Reloid(), urec->Relfilenode(), urec->Utype(), urec->Uinfo());
securec_check_ss(rc, "\0", "\0");
if ((urec->Uinfo() & UNDO_UREC_INFO_PAYLOAD) != 0) {
rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN,
"\n\t\tflag_payload, size = %lu.\n", SIZE_OF_UNDO_RECORD_PAYLOAD);
securec_check_ss(rc, "\0", "\0");
}
if ((urec->Uinfo() & UNDO_UREC_INFO_TRANSAC) != 0) {
rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN,
"\n\t\tflag_transac, size = %lu.\n", SIZE_OF_UNDO_RECORD_TRANSACTION);
securec_check_ss(rc, "\0", "\0");
}
if ((urec->Uinfo() & UNDO_UREC_INFO_BLOCK) != 0) {
rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN,
"\n\t\tflag_block, size = %lu.\n", SIZE_OF_UNDO_RECORD_BLOCK);
securec_check_ss(rc, "\0", "\0");
}
if ((urec->Uinfo() & UNDO_UREC_INFO_OLDTD) != 0) {
rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN,
"\n\t\tflag_oldtd, size = %lu.\n", SIZE_OF_UNDO_RECORD_OLDTD);
securec_check_ss(rc, "\0", "\0");
}
if ((urec->Uinfo() & UNDO_UREC_INFO_CONTAINS_SUBXACT) != 0) {
rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN,
"\n\t\tflag_subxact.\n");
securec_check_ss(rc, "\0", "\0");
}
if ((urec->Uinfo() & UNDO_UREC_INFO_HAS_PARTOID) != 0) {
rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN,
"\n\t\tflag_partoid, size = %lu.\n", SIZE_OF_UNDO_RECORD_PARTITION);
securec_check_ss(rc, "\0", "\0");
}
if ((urec->Uinfo() & UNDO_UREC_INFO_HAS_TABLESPACEOID) != 0) {
rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN,
"\n\t\tflag_tablespaceoid, size = %lu.\n", SIZE_OF_UNDO_RECORD_TABLESPACE);
securec_check_ss(rc, "\0", "\0");
}
rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN,
"\n\t\twblk = blk_prev(%lu), blockno(%u), offset(%u).\n", urec->Blkprev(), urec->Blkno(), urec->Offset());
securec_check_ss(rc, "\0", "\0");
rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN,
"\n\t\twtxn = prevurp(%lu).\n", urec->Prevurp2());
securec_check_ss(rc, "\0", "\0");
rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN,
"\n\t\twpay = payloadlen(%u).\n", urec->PayLoadLen());
securec_check_ss(rc, "\0", "\0");
rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN,
"\n\t\twtd = oldcaxtid(%lu).\n", urec->OldXactId());
securec_check_ss(rc, "\0", "\0");
rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN,
"\n\t\twpart_ = partitionoid(%u).\n", urec->Partitionoid());
securec_check_ss(rc, "\0", "\0");
rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN,
"\n\t\twtspc_ = tablespace(%u).\n", urec->Tablespace());
securec_check_ss(rc, "\0", "\0");
rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN,
"\n\t\tUndo diskTuple: td_id %u, reserved %u, flag %u, flag2 %u, t_hoff %u.\n",
diskTuple->td_id, diskTuple->reserved, diskTuple->flag, diskTuple->flag2, diskTuple->t_hoff);
securec_check_ss(rc, "\0", "\0");
}
}
if (prevTdSlot == currentTdSlot) {
if (IS_VALID_UNDO_REC_PTR(urec->Blkprev())) {
currUrp = urec->Blkprev();
DELETE_EX(urec);
CHECK_FOR_INTERRUPTS();
goto fetch_undo;
}
} else {
if (currentTdSlot == UHEAPTUP_SLOT_FROZEN) {
goto fetch_end;
} else {
prevTdSlot = currentTdSlot;
UHeapPageTDData *tdPtr = (UHeapPageTDData *)PageGetTDPointer(page);
UHeapPageHeaderData *phdr = (UHeapPageHeaderData *)page;
if (currentTdSlot < 1 || currentTdSlot > phdr->td_count) {
ereport(WARNING,
(errcode(ERRCODE_OPERATE_FAILED),
errmodule(MOD_USTORE),
errmsg("CurrentTd(%d) is out of td(%d) in page header", currentTdSlot, phdr->td_count),
errdetail("N/A"),
errcause("Td slot is corrupted"),
erraction("N/A")));
goto fetch_end;
} else {
TD *thistrans = &tdPtr->td_info[currentTdSlot - 1];
currUrp = thistrans->undo_record_ptr;
DELETE_EX(urec);
CHECK_FOR_INTERRUPTS();
goto fetch_undo;
}
}
}
fetch_end:
DELETE_EX(urec);
return;
}
static void ParseUHeapPageItemUndo(void *page, ItemPointer ctid, char *output)
{
if (page == NULL || ctid == NULL || output == NULL) {
return;
}
int tdSlot = 0;
errno_t rc = EOK;
UHeapPageHeader pHeader = (UHeapPageHeader) page;
UHeapPageTDData *tdPtr = (UHeapPageTDData *)PageGetTDPointer(page);
RowPtr *rp = UPageGetRowPtr(pHeader, ctid->ip_posid);
if (RowPtrIsNormal(rp)) {
UHeapDiskTuple udiskTuple = NULL;
UHeapDiskTupleData inputDiskTuple;
TD* tdInfo = NULL;
udiskTuple = (UHeapDiskTuple) UPageGetRowData(pHeader, rp);
Assert(udiskTuple != NULL);
tdSlot = udiskTuple->td_id;
if (tdSlot == UHEAPTUP_SLOT_FROZEN) {
return;
}
tdInfo = &(tdPtr->td_info[tdSlot - 1]);
rc = memset_s((char *) &inputDiskTuple, SizeOfUHeapDiskTupleData, 0, SizeOfUHeapDiskTupleData);
securec_check_c(rc, "\0", "\0");
rc = memcpy_s(&inputDiskTuple, SizeOfUHeapDiskTupleData, udiskTuple, SizeOfUHeapDiskTupleData);
securec_check_c(rc, "\0", "\0");
DumpUndoRecordByUrpFromPage(page, ctid, tdInfo->undo_record_ptr, tdSlot, &inputDiskTuple, output);
} else if (RowPtrIsDeleted(rp)) {
TD* tdInfo = NULL;
UHeapDiskTupleData inputDiskTuple;
tdSlot = RowPtrGetTDSlot(rp);
if (tdSlot == UHEAPTUP_SLOT_FROZEN) {
return;
}
tdInfo = &(tdPtr->td_info[tdSlot - 1]);
rc = memset_s((char *) &inputDiskTuple, SizeOfUHeapDiskTupleData, 0, SizeOfUHeapDiskTupleData);
securec_check_c(rc, "\0", "\0");
DumpUndoRecordByUrpFromPage(page, ctid, tdInfo->undo_record_ptr, tdSlot, &inputDiskTuple, output);
}
}
static void ParseUHeapPageData(void *page, BlockNumber blkno, char *output, bool dumpUndo)
{
errno_t rc;
uint32 rowPtrCnt = 0;
uint32 storeCnt = 0;
uint32 unusedCnt = 0;
uint32 normalCnt = 0;
uint32 deadCnt = 0;
uint32 redirectCnt = 0;
Item item;
RowPtr *rowptr;
UHeapTupleData utuple;
UHeapPageHeader pageHeader = NULL;
if (page == NULL || output == NULL) {
return;
}
pageHeader = (UHeapPageHeader)page;
if (pageHeader->pd_lower <= SizeOfUHeapPageHeaderData) {
rowPtrCnt = 0;
} else {
rowPtrCnt =
(pageHeader->pd_lower - (SizeOfUHeapPageHeaderData + SizeOfUHeapTDData(pageHeader))) / sizeof(RowPtr);
}
rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN,
"\n\tUHeap tuple information on this page\n");
securec_check_ss(rc, "\0", "\0");
for (OffsetNumber i = FirstOffsetNumber; i <= rowPtrCnt; i++) {
rowptr = UPageGetRowPtr(pageHeader, i);
if (RowPtrIsUsed(rowptr)) {
if (RowPtrHasStorage(rowptr)) {
storeCnt++;
}
if (RowPtrIsNormal(rowptr)) {
rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN,
"\n\t\tTuple #%u is normal: length %u, offset %u\n", i, RowPtrGetLen(rowptr),
RowPtrGetOffset(rowptr));
securec_check_ss(rc, "\0", "\0");
normalCnt++;
item = UPageGetRowData(pageHeader, rowptr);
UHeapTupleCopyBaseFromPage(&utuple, pageHeader);
ParseUHeapPageItem(item, &utuple, output);
if (dumpUndo) {
ItemPointerData ctid;
ItemPointerSet(&ctid, blkno, i);
ParseUHeapPageItemUndo(page, &ctid, output);
}
} else if (RowPtrIsDead(rowptr)) {
rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN,
"\n\t\tTuple #%u is dead: length %u, offset %u", i, RowPtrGetLen(rowptr), RowPtrGetOffset(rowptr));
securec_check_ss(rc, "\0", "\0");
deadCnt++;
} else {
rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN,
"\n\t\tTuple #%u is redirected: length %u, offset %u", i, RowPtrGetLen(rowptr),
RowPtrGetOffset(rowptr));
securec_check_ss(rc, "\0", "\0");
redirectCnt++;
}
} else {
unusedCnt++;
rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "\n\t\tTuple #%u is unused\n", i);
securec_check_ss(rc, "\0", "\0");
}
}
rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN,
"\tSummary (%u total): %u unused, %u normal, %u dead, %u redirect\n", rowPtrCnt, unusedCnt, normalCnt, deadCnt,
redirectCnt);
securec_check_ss(rc, "\0", "\0");
}
static void ParseUHeapPageSpecialInfo(void *page, char *output)
{
errno_t rc = 0;
if (page == NULL || output == NULL) {
return;
}
rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN,
"\n\tSpecial area information on this page\n");
securec_check_ss(rc, "\0", "\0");
rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "\n\tNot used currently.\n");
securec_check_ss(rc, "\0", "\0");
}
static void ParseUHeapPage(void *page, BlockNumber blkno, BlockNumber endBlk, char *output, bool dumpUndo)
{
uint16 pageHeaderSize = 0;
UHeapPageHeader pageHeader = NULL;
errno_t rc;
if (page == NULL || output == NULL) {
return;
}
/* Parse header of uheap page. */
pageHeader = (UHeapPageHeader)page;
/* Check invalidation of uheap page head. */
pageHeaderSize = GetPageHeaderSize(pageHeader);
if (pageHeader->pd_lower < pageHeaderSize || pageHeader->pd_lower > pageHeader->pd_upper ||
pageHeader->pd_upper > pageHeader->pd_special || pageHeader->pd_special > BLCKSZ ||
pageHeader->pd_special != MAXALIGN(pageHeader->pd_special)) {
rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN,
"The page data is corrupted, corrupted page pointers: lower = %u, upper = %u, special = %u\n",
pageHeader->pd_lower, pageHeader->pd_upper, pageHeader->pd_special);
securec_check_ss(rc, "\0", "\0");
return;
}
ParseUHeapPageHeader((void *)pageHeader, blkno, endBlk, output);
if (PageIsNew(pageHeader)) {
return;
}
/* Parse td slot info of uheap page. */
ParseUHeapPageTDInfo(pageHeader, output);
/* Parse items of uheap page. */
ParseUHeapPageData(pageHeader, blkno, output, dumpUndo);
/* Parse special area of uheap page. */
ParseUHeapPageSpecialInfo(pageHeader, output);
}
static void ParseIndexPageHeader(void *page, int type, BlockNumber blkno, BlockNumber endBlk, char *output)
{
errno_t rc = 0;
bool chksumResult = false;
PageHeader pageHeader = NULL;
if (page == NULL || output == NULL) {
return;
}
pageHeader = (PageHeader)page;
rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN,
"%s index page information of block %u/%u\n", (type == BTREE_INDEX) ? "Btree" : "UBtree", blkno, endBlk);
securec_check_ss(rc, "\0", "\0");
rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "\tpd_lsn: %X/%X\n",
(uint32)(PageGetLSN((Page)pageHeader) >> XIDTHIRTYTWO), (uint32)PageGetLSN((Page)pageHeader));
securec_check_ss(rc, "\0", "\0");
if (CheckPageZeroCases(pageHeader)) {
uint16 checksum = pg_checksum_page((char *)pageHeader, (BlockNumber)blkno);
chksumResult = (checksum == pageHeader->pd_checksum);
}
rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "\tpd_checksum: 0x%X, verify %s\n",
pageHeader->pd_checksum, chksumResult ? "success" : "fail");
securec_check_ss(rc, "\0", "\0");
if (PageHasFreeLinePointers((Page)pageHeader)) {
rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "PD_HAS_FREE_LINES.");
securec_check_ss(rc, "\0", "\0");
}
if (PageIsFull(pageHeader)) {
rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "PD_PAGE_FULL.");
securec_check_ss(rc, "\0", "\0");
}
if (PageIsAllVisible(pageHeader)) {
rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "PD_ALL_VISIBLE.");
securec_check_ss(rc, "\0", "\0");
}
if (PageIsCompressed(pageHeader)) {
rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "PD_COMPRESSED_PAGE.");
securec_check_ss(rc, "\0", "\0");
}
if (PageIsLogical(pageHeader)) {
rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "PD_LOGICAL_PAGE.");
securec_check_ss(rc, "\0", "\0");
}
if (PageIsEncrypt(pageHeader)) {
rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "PD_ENCRYPT_PAGE.");
securec_check_ss(rc, "\0", "\0");
}
rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN,
"\tpd_lower: %u, %s\n\tpd_upper: %u, %s\n\tpd_special: %u, size %u\n", pageHeader->pd_lower,
PageIsEmpty((Page)pageHeader) ? "empty" : "non-empty", pageHeader->pd_upper,
PageIsNew((Page)pageHeader) ? "new page" : "old page", pageHeader->pd_special,
PageGetSpecialSize((Page)pageHeader));
securec_check_ss(rc, "\0", "\0");
rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "\tPage size & version: %u, %u\n",
(uint16)PageGetPageSize(page), (uint16)PageGetPageLayoutVersion(page));
securec_check_ss(rc, "\0", "\0");
rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "\tpd_prune_xid: %lu\n",
pageHeader->pd_prune_xid + ((HeapPageHeader)(pageHeader))->pd_xid_base);
securec_check_ss(rc, "\0", "\0");
rc =
snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "\tpd_xid_base: %lu, pd_multi_base: %lu\n",
((HeapPageHeader)(pageHeader))->pd_xid_base, ((HeapPageHeader)(pageHeader))->pd_multi_base);
securec_check_ss(rc, "\0", "\0");
return;
}
static void ParseIndexPageItem(Item item, int type, uint32 len, char *output)
{
if (item == NULL || output == NULL) {
return;
}
errno_t rc = 0;
IndexTuple itup = (IndexTuple)item;
bool hasnull = (itup->t_info & INDEX_NULL_MASK);
unsigned int tuplen = (itup->t_info & INDEX_SIZE_MASK);
if (tuplen != len) {
if (type == UBTREE_INDEX) {
UstoreIndexXid uxid = (UstoreIndexXid)UstoreIndexTupleGetXid(itup);
rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN,
"\t\t\txmin:%d xmax:%d Heap Tid: block %u/%u, offset %u\n", uxid->xmin, uxid->xmax,
itup->t_tid.ip_blkid.bi_hi, itup->t_tid.ip_blkid.bi_lo, itup->t_tid.ip_posid);
securec_check_ss(rc, "\0", "\0");
}
} else {
rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN,
"\t\t\tHeap Tid: block %u/%u, offset %u\n", itup->t_tid.ip_blkid.bi_hi, itup->t_tid.ip_blkid.bi_lo,
itup->t_tid.ip_posid);
securec_check_ss(rc, "\0", "\0");
}
rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "\t\t\tLength: %u", tuplen);
securec_check_ss(rc, "\0", "\0");
if (itup->t_info & INDEX_VAR_MASK) {
rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, ", has var-width attrs");
securec_check_ss(rc, "\0", "\0");
}
if (hasnull) {
rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, ", has nulls ");
securec_check_ss(rc, "\0", "\0");
}
rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "\n");
securec_check_ss(rc, "\0", "\0");
return;
}
static void ParseIndexPageSpecialInfo(void *page, int type, char *output)
{
errno_t rc = 0;
BTPageOpaqueInternal opaque = NULL;
UBTPageOpaqueInternal uopaque = NULL;
PageHeader pageHeader = NULL;
if (page == NULL || output == NULL || type >= INDEX_BOTT) {
return;
}
pageHeader = (PageHeader)page;
opaque = (BTPageOpaqueInternal)PageGetSpecialPointer((Page)pageHeader);
if (PageGetSpecialSize((Page)pageHeader) > MAXALIGN(sizeof(BTPageOpaqueData))) {
uopaque = (UBTPageOpaqueInternal)opaque;
}
rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "\n%s index special information:\n",
(type == BTREE_INDEX) ? "BTree" : "UBTree");
securec_check_ss(rc, "\0", "\0");
rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "\tbtree left sibling: %u\n",
opaque->btpo_prev);
securec_check_ss(rc, "\0", "\0");
rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "\tbtree right sibling: %u\n",
opaque->btpo_next);
securec_check_ss(rc, "\0", "\0");
if (!P_ISDELETED(opaque)) {
rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "\tbtree tree level: %u\n",
opaque->btpo.level);
securec_check_ss(rc, "\0", "\0");
} else {
if (uopaque) {
rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "\tnext txid (deleted): %lu\n",
((UBTPageOpaque)uopaque)->xact);
securec_check_ss(rc, "\0", "\0");
} else {
rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "\tnext txid (deleted): %lu\n",
((BTPageOpaque)opaque)->xact);
securec_check_ss(rc, "\0", "\0");
}
}
rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "\tbtree flag: ");
securec_check_ss(rc, "\0", "\0");
if (P_ISLEAF(opaque)) {
rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "BTP_LEAF ");
securec_check_ss(rc, "\0", "\0");
} else {
rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "BTP_INTERNAL ");
securec_check_ss(rc, "\0", "\0");
}
if (P_ISROOT(opaque)) {
rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "BTP_ROOT ");
securec_check_ss(rc, "\0", "\0");
}
if (P_ISDELETED(opaque)) {
rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "BTP_DELETED ");
securec_check_ss(rc, "\0", "\0");
}
if (P_ISHALFDEAD(opaque)) {
rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "BTP_HALF_DEAD ");
securec_check_ss(rc, "\0", "\0");
}
if (P_HAS_GARBAGE(opaque)) {
rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "BTP_HAS_GARBAGE ");
securec_check_ss(rc, "\0", "\0");
}
rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "\n");
securec_check_ss(rc, "\0", "\0");
rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "\tbtree cycle ID: %u\n ",
opaque->btpo_cycleid);
securec_check_ss(rc, "\0", "\0");
if (uopaque) {
rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "\tubtree active tuples: %d\n",
uopaque->activeTupleCount);
securec_check_ss(rc, "\0", "\0");
}
return;
}
static void ParseIndexPageData(void *page, int type, char *output)
{
errno_t rc;
uint32 rowPtrCnt = 0;
uint32 storeCnt = 0;
uint32 notusedCnt = 0;
uint32 normalCnt = 0;
uint32 deadCnt = 0;
uint32 redirectCnt = 0;
Item item;
ItemId lp;
PageHeader pageHeader = NULL;
if (page == NULL || output == NULL) {
return;
}
pageHeader = (PageHeader)page;
rowPtrCnt = PageGetMaxOffsetNumber((Page)pageHeader);
rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN,
"\n\tIndex tuple information on this page\n");
securec_check_ss(rc, "\0", "\0");
for (uint32 i = FirstOffsetNumber; i <= rowPtrCnt; i++) {
lp = PageGetItemId((Page)pageHeader, i);
if (ItemIdIsUsed(lp)) {
if (ItemIdHasStorage(lp)) {
storeCnt++;
}
if (ItemIdIsNormal(lp) || (IndexItemIdIsFrozen(lp))) {
rc = ItemIdIsNormal(lp) ? (snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN,
"\n\t\tTuple #%u is normal: length %u, offset %u\n", i, ItemIdGetLength(lp), ItemIdGetOffset(lp))) :
(snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN,
"\n\t\tTuple #%u is frozen: length %u, offset %u\n", i, ItemIdGetLength(lp), ItemIdGetOffset(lp)));
securec_check_ss(rc, "\0", "\0");
normalCnt++;
item = PageGetItem((Page)pageHeader, lp);
ParseIndexPageItem(item, type, ItemIdGetLength(lp), output);
} else if (ItemIdIsDead(lp)) {
rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN,
"\n\t\tTuple #%u is dead: length %u, offset %u", i, ItemIdGetLength(lp), ItemIdGetOffset(lp));
securec_check_ss(rc, "\0", "\0");
deadCnt++;
} else {
rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN,
"\n\t\tTuple #%u is redirected: length %u, offset %u", i, ItemIdGetLength(lp), ItemIdGetOffset(lp));
securec_check_ss(rc, "\0", "\0");
redirectCnt++;
}
} else {
notusedCnt++;
rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN, "\n\t\tTuple #%u is unused\n", i);
securec_check_ss(rc, "\0", "\0");
}
}
rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN,
"\tSummary (%u total): %u unused, %u normal, %u dead, %u redirect\n", rowPtrCnt, notusedCnt, normalCnt, deadCnt,
redirectCnt);
securec_check_ss(rc, "\0", "\0");
return;
}
static void ParseIndexPage(void *page, int type, BlockNumber blkno, BlockNumber endBlk, char *output)
{
PageHeader pageHeader = NULL;
uint16 headersize = 0;
errno_t rc = 0;
if (page == NULL || output == NULL) {
return;
}
pageHeader = (PageHeader)page;
headersize = GetPageHeaderSize(pageHeader);
if (pageHeader->pd_lower < headersize || pageHeader->pd_lower > pageHeader->pd_upper ||
pageHeader->pd_upper > pageHeader->pd_special || pageHeader->pd_special > BLCKSZ ||
pageHeader->pd_special != MAXALIGN(pageHeader->pd_special)) {
rc = snprintf_s(output + (int)strlen(output), MAXOUTPUTLEN, MAXOUTPUTLEN,
"The page data is corrupted, corrupted page pointers: lower = %u, upper = %u, special = %u\n",
pageHeader->pd_lower, pageHeader->pd_upper, pageHeader->pd_special);
securec_check_ss(rc, "\0", "\0");
return;
}
ParseIndexPageHeader((void *)page, type, blkno, endBlk, output);
if (PageIsNew(pageHeader)) {
return;
}
ParseIndexPageData((void *)page, type, output);
ParseIndexPageSpecialInfo((void *)page, type, output);
return;
}
Datum gs_parse_page_bypath(PG_FUNCTION_ARGS)
{
/* check user's right */
const char fName[MAXFNAMELEN] = "gs_parse_page_bypath";
CheckUser(fName);
/* read in parameters */
char *path = text_to_cstring(PG_GETARG_TEXT_P(0));
int64 blocknum = PG_GETARG_INT64(1);
char *relation_type = text_to_cstring(PG_GETARG_TEXT_P(2));
bool read_memory = PG_GETARG_BOOL(3);
/* In order to avoid querying the shared buffer and applying LW locks, blocking the business. */
/* In the case of finding all pages, force to check disk */
if (blocknum == -1) {
read_memory = false;
}
char *outputFilename = ParsePage(path, blocknum, relation_type, read_memory);
PG_RETURN_TEXT_P(cstring_to_text(outputFilename));
}