/* --------------------------------------------------------------------------------------- * * * 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 #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)); }