openGauss-server/src/gausskernel/storage/page/gs_xlogdump.cpp

620 lines
28 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* ---------------------------------------------------------------------------------------
* *
* 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.
* ---------------------------------------------------------------------------------------
*
* gs_xlogdump.cpp
*
* IDENTIFICATION
* src/gausskernel/storage/page/gs_xlogdump.cpp
*
* ---------------------------------------------------------------------------------------
*/
#include <stdio.h>
#include "c.h"
#include "access/tupdesc.h"
#include "access/xact.h"
#include "access/xlog_basic.h"
#include "access/xlogdefs.h"
#include "access/xlogreader.h"
#include "access/xlog_internal.h"
#include "catalog/catalog.h"
#include "lib/stringinfo.h"
#include "nodes/pg_list.h"
#include "postgres.h"
#include "storage/smgr/relfilenode.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
#include "utils/palloc.h"
#include "pageparse.h"
typedef struct XLogPrivate {
const char *datadir;
TimeLineID tli;
} XLogPrivate;
typedef struct XLogFilter {
TransactionId by_xid;
bool by_xid_enabled;
bool by_tablepath_enabled;
bool by_block;
RelFileNode by_relfilenode;
BlockNumber blocknum;
} XLogFilter;
//通过组合数据目录路径、起始LSN和结束LSN生成一个格式化的输出文件名
static void GenerateOutputFileName(char *outputFilename, char *start_lsn_str, char *end_lsn_str)
{
List *elemlist = NIL;
//将起始LSN字符串按照'/'进行分割将元素存储在elemlist中
SplitIdentifierString(start_lsn_str, '/', &elemlist);
//获取elemlist中第二个元素赋值给start_lsn_str_p2变量
char *start_lsn_str_p2 = (char *)lsecond(elemlist);
//释放elemlist占用的内存
list_free_ext(elemlist);
//将结束LSN字符串按照'/'进行分割将元素存储在elemlist中
SplitIdentifierString(end_lsn_str, '/', &elemlist);
//获取elemlist中第二个元素赋值给end_lsn_str_p2变量
char *end_lsn_str_p2 = (char *)lsecond(elemlist);
//格式化输出文件名并将结果写入outputFilename的末尾
int rc = snprintf_s(outputFilename + (int)strlen(outputFilename), MAXFILENAME, MAXFILENAME - 1, "%s/%s_%s.xlog",
t_thrd.proc_cxt.DataDir, start_lsn_str_p2, end_lsn_str_p2);
//检查格式化操作是否成功执行
securec_check_ss(rc, "\0", "\0");
}
//验证LSN字符串并将其转换为XLogRecPtr类型的LSN指针
static void ValidateLSN(char *lsn_str, XLogRecPtr *lsn_ptr)
{
uint32 hi = 0;
uint32 lo = 0;
//调用validate_xlog_location函数验证LSN字符串的有效性
validate_xlog_location(lsn_str);
//使用sscanf_s函数从LSN字符串中解析出hi和lo两个部分的值
if (sscanf_s(lsn_str, "%X/%X", &hi, &lo) != TWO)
//如果解析失败则抛出错误并提示无法解析LSN字符串
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("could not parse xlog location \"%s\"", lsn_str)));
//将hi和lo组合成64位的LSN值存储到lsn_ptr中
*lsn_ptr = (((uint64)hi) << XIDTHIRTYTWO) | lo;
}
//验证起始LSN和结束LSN并将它们转换为XLogRecPtr类型的LSN指针
static void ValidateStartEndLSN(char *start_lsn_str, char *end_lsn_str, XLogRecPtr *start_lsn, XLogRecPtr *end_lsn)
{
//验证起始LSN字符串并将其转换为XLogRecPtr类型的起始LSN指针
ValidateLSN(start_lsn_str, start_lsn);
//验证结束LSN字符串并将其转换为XLogRecPtr类型的结束LSN指针
ValidateLSN(end_lsn_str, end_lsn);
//检查结束LSN是否小于起始LSN如果是则抛出错误
if (XLByteLT(*end_lsn, *start_lsn))
/*
* 如果结束LSN小于起始LSN则抛出错误。
* 错误信息包含起始LSN和结束LSN的具体数值。
* 使用% X格式将高位32位和低位32位转换为十六进制数值。
*/
ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("start xlog location %X/%X should be smaller than or equal to end xlog location %X/%X",
(uint32)(*start_lsn >> XIDTHIRTYTWO), (uint32)(*start_lsn), (uint32)(*end_lsn >> XIDTHIRTYTWO),
(uint32)(*end_lsn))));
}
//获取当前数据库日志的最小LSN用于作为起始LSN的参考值
static XLogRecPtr GetMinLSN()
{
//获取最后一个已移除的日志段号
XLogSegNo lastRemovedSegNo = XLogGetLastRemovedSegno();
//计算当前LSN
XLogRecPtr current_recptr = (lastRemovedSegNo + 1) * XLogSegSize;
//返回当前LSN
return current_recptr;
}
//获取当前数据库日志的最大LSN用于作为结束LSN的参考值
static XLogRecPtr GetMaxLSN()
{
//如果正在进行恢复则抛出错误并提示无法获取最大LSN
if (RecoveryInProgress())
ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("recovery is in progress"),
errhint("Can't get local max LSN during recovery.")));
//获取当前日志记录的写入指针位置
XLogRecPtr current_recptr = GetXLogWriteRecPtr();
//返回当前日志记录的写入指针位置即最大LSN
return current_recptr;
}
//将XLog记录信息输出到字符串
static void XLogDumpDisplayRecord(XLogReaderState *record, char *strOutput)
{
errno_t rc = snprintf_s(strOutput + (int)strlen(strOutput), MAXOUTPUTLEN, MAXOUTPUTLEN - 1,
"start_lsn: %X/%X \nend_lsn: %X/%X \nxid: " XID_FMT " \nterm: %u \ntotal length: %u \ndesc: %s - ",
(uint32)(record->ReadRecPtr >> XIDTHIRTYTWO), (uint32)record->ReadRecPtr,
(uint32)(record->EndRecPtr >> XIDTHIRTYTWO), (uint32)record->EndRecPtr, XLogRecGetXid(record),
XLogRecGetTerm(record), XLogRecGetTotalLen(record), RmgrTable[XLogRecGetRmid(record)].rm_name);
securec_check_ss(rc, "\0", "\0");
StringInfoData buf;
initStringInfo(&buf);
RmgrTable[XLogRecGetRmid(record)].rm_desc(&buf, record);
//输出块引用的信息
rc = strcat_s(strOutput, MAXOUTPUTLEN, buf.data);
securec_check(rc, "\0", "\0");
//如果是bucket文件节点输出bucketNode信息
if (!XLogRecHasAnyBlockRefs(record)) {
rc = strcat_s(strOutput, MAXOUTPUTLEN, "\n\n");
securec_check(rc, "\0", "\0");
return;
}
if (record->max_block_id >= 0) {
rc = strcat_s(strOutput, MAXOUTPUTLEN, "\nothers: ");
securec_check(rc, "\0", "\0");
}
for (int block_id = 0; block_id <= record->max_block_id; block_id++) {
if (!XLogRecHasBlockRef(record, block_id))
continue;
/* Print format: others: rel %u/%u/%u/%d fork %s blk %u */
RelFileNode rnode;
ForkNumber forknum;
BlockNumber blk;
if (!XLogRecGetBlockTag(record, block_id, &rnode, &forknum, &blk))
continue;
rc = snprintf_s(strOutput + (int)strlen(strOutput), MAXOUTPUTLEN, MAXOUTPUTLEN - 1, "\n\trel %u/%u/%u",
rnode.spcNode, rnode.dbNode, rnode.relNode);
securec_check_ss(rc, "\0", "\0");
if (IsBucketFileNode(rnode)) { /* check between InvalidBktId and SegmentBktId */
rc = snprintf_s(strOutput + (int)strlen(strOutput), MAXOUTPUTLEN, MAXOUTPUTLEN - 1,
"/%d", rnode.bucketNode);
securec_check_ss(rc, "\0", "\0");
}
rc = snprintf_s(strOutput + (int)strlen(strOutput), MAXOUTPUTLEN, MAXOUTPUTLEN - 1, ", fork %s",
forkNames[forknum]);
securec_check_ss(rc, "\0", "\0");
rc = snprintf_s(strOutput + (int)strlen(strOutput), MAXOUTPUTLEN, MAXOUTPUTLEN - 1, ", blk %u", blk);
securec_check_ss(rc, "\0", "\0");
/* others: lastlsn %X/%X" */
// 输出lastlsn信息
XLogRecPtr lsn;
XLogRecGetBlockLastLsn(record, block_id, &lsn);
rc = snprintf_s(strOutput + (int)strlen(strOutput), MAXOUTPUTLEN, MAXOUTPUTLEN - 1, ", lastlsn %X/%X",
(uint32)(lsn >> XIDTHIRTYTWO), (uint32)lsn);
securec_check_ss(rc, "\0", "\0");
}
rc = strcat_s(strOutput, MAXOUTPUTLEN, "\n\n");
securec_check(rc, "\0", "\0");
}
void CheckOpenFile(FILE *outputfile, char *outputFilename)
{
// 检查文件打开操作的结果
if (outputfile == NULL)
ereport(ERROR, (errcode(ERRCODE_FILE_READ_FAILED), (errmsg("Cannot read %s", outputFilename))));
}
void CheckWriteFile(int result, int cnt_len, char *outputFilename)
{
// 检查文件写入操作的结果
if (result != cnt_len)
ereport(ERROR, (errcode(ERRCODE_FILE_WRITE_FAILED), (errmsg("Cannot write %s", outputFilename))));
}
void CheckCloseFile(int result, char *outputFilename)
{
//检查文件关闭操作的结果
if (0 != result)
ereport(ERROR, (errcode(ERRCODE_IO_ERROR), (errmsg("Cannot close %s", outputFilename))));
}
//检查当前XLog记录是否符合一组给定的筛选条件
static bool CheckValidRecord(XLogReaderState *xlogreader_state, XLogFilter *filter)
{
bool found = false;//初始化 found 变量,表示是否找到满足条件的块
//循环遍历 XLog 中的每个块
for (int i = 0; i <= xlogreader_state->max_block_id; i++) {
RelFileNode rnode;
ForkNumber forknum;
BlockNumber blk;
//获取当前块的标记
if (!XLogRecGetBlockTag(xlogreader_state, i, &rnode, &forknum, &blk))
continue;//如果返回 false则说明没有找到块标记跳过该块继续循环
//检查块标记是否符合查询条件
if (RelFileNodeEquals(rnode, filter->by_relfilenode))
/* if equal to specific block or check all blocks, found = ture; */
found = (!filter->by_block) || (blk == filter->blocknum);
//如果找到一个符合条件的块则返回true表示当前记录有效
if (found) {
return found;
}
}
//没有找到任何符合条件的块则返回false表示当前记录无效
return found;
}
//在一系列连续的日志记录中查找下一个有效的日志记录的LSN
static XLogRecPtr UpdateNextLSN(XLogRecPtr cur_lsn, XLogRecPtr end_lsn, XLogReaderState *xlogreader_state, bool *found)
{
XLogRecPtr next_record = InvalidXLogRecPtr;// 初始化 next_record表示下一个记录的LSN
// 使用循环尝试多次查找下一个记录的LSN
for (int tryTimes = 0; tryTimes < FIVE; tryTimes++) {
// 计算起始 LSN
XLogRecPtr start_lsn = Max(cur_lsn, (g_instance.comm_cxt.predo_cxt.redoPf.oldest_segment) * XLogSegSize);
// 查找下一个记录的LSN
next_record = XLogFindNextRecord(xlogreader_state, start_lsn);
// 检查找到的下一个记录的LSN是否有效并且小于结束LSN
if (!XLByteEQ(next_record, InvalidXLogRecPtr) && XLByteLT(next_record, end_lsn)) {
*found = true;// 设置 found 为 true表示找到了下一个记录的LSN
return next_record;// 返回找到的下一个记录的LSN
}
}
// 如果没有找到下一个记录的LSN则返回 InvalidXLogRecPtr
return next_record;
}
/*
start_lsn: 起始位置的LSNLog Sequence Number
end_lsn: 结束位置的LSN
filter: 筛选器,用于选择要输出的记录
outputFilename: 目标输出文件的名称
readprivate: 一个XLogPrivate类型的结构体用于初始化XLogReaderState
xlogreader_state: XLogReaderState类型的对象用于读取WAL文件
first_record: 第一个有效记录的位置,即起始位置之后的第一个有效记录位置
valid_start_lsn: 真正处理的起始位置,由于可能找不到第一个有效记录,因此可能与给定的起始位置不同。
valid_end_lsn: 当前读取的有效记录的结束位置
outputfile: 目标输出文件的指针
strOutput: 字符串缓冲区,用于记录输出信息
count: 输出记录的计数器
errormsg: 出错时的错误信息
record: XLogRecord类型的对象表示读取的记录
*/
//将WALWrite-Ahead Log文件中的记录转储到指定的输出文件中
static void XLogDump(XLogRecPtr start_lsn, XLogRecPtr end_lsn, XLogFilter *filter, char *outputFilename)
{
/* start reading */
// 初始化XLogReaderState用于读取WAL文件
errno_t rc = EOK;
XLogPrivate readprivate;
rc = memset_s(&readprivate, sizeof(XLogPrivate), 0, sizeof(XLogPrivate));
securec_check_c(rc, "\0", "\0");
readprivate.datadir = t_thrd.proc_cxt.DataDir;
readprivate.tli = 1;
XLogReaderState *xlogreader_state = XLogReaderAllocate(&SimpleXLogPageRead, &readprivate);
if (!xlogreader_state)
ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_RESOURCES),
(errmsg("memory is temporarily unavailable while allocate xlog reader"))));
/* get the first valid xlog record location */
// 找到起始位置之后的第一个有效记录位置
XLogRecPtr first_record = XLogFindNextRecord(xlogreader_state, start_lsn);
/* if we are recycling or removing log files concurrently, we can't find the next record right after.
* Hence, we need to update the min_lsn */
// 如果找不到第一个有效记录则更新min_lsn并再次尝试查找
if (XLByteEQ(first_record, InvalidXLogRecPtr)) {
ereport(WARNING, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
(errmsg("XLogFindNextRecord: could not find a valid record after %X/%X. Retry.",
(uint32)(start_lsn >> XIDTHIRTYTWO), (uint32)start_lsn))));
bool found = false;
first_record = UpdateNextLSN(start_lsn, end_lsn, xlogreader_state, &found);
if (!found)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
(errmsg("XLogFindNextRecord: could not find a valid record between %X/%X and %X/%X.",
(uint32)(start_lsn >> XIDTHIRTYTWO), (uint32)start_lsn,
(uint32)(end_lsn >> XIDTHIRTYTWO), (uint32)end_lsn))));
}
XLogRecPtr valid_start_lsn = first_record;
XLogRecPtr valid_end_lsn = valid_start_lsn;
// 打开目标输出文件
FILE *outputfile = fopen(outputFilename, "w");
CheckOpenFile(outputfile, outputFilename);
// 输出一些转储信息
char *strOutput = (char *)palloc(MAXOUTPUTLEN * sizeof(char));
rc = memset_s(strOutput, MAXOUTPUTLEN, 0, MAXOUTPUTLEN);
securec_check(rc, "\0", "\0");
/* valid first record is not the given one */
if (!XLByteEQ(first_record, start_lsn) && (start_lsn % XLogSegSize) != 0) {
rc = snprintf_s(strOutput + (int)strlen(strOutput), MAXOUTPUTLEN, MAXOUTPUTLEN - 1,
"first record is after %X/%X, at %X/%X, skipping over %lu bytes\n", (uint32)(start_lsn >> XIDTHIRTYTWO),
(uint32)start_lsn, (uint32)(first_record >> XIDTHIRTYTWO), (uint32)first_record,
XLByteDifference(first_record, start_lsn));
securec_check_ss(rc, "\0", "\0");
}
CheckWriteFile(fwrite(strOutput, 1, strlen(strOutput), outputfile), strlen(strOutput), outputFilename);
pfree_ext(strOutput);
int count = 0;
char *errormsg = NULL;
XLogRecord *record = NULL;
// 不断读取WAL记录直到结束位置
while (XLByteLT(xlogreader_state->EndRecPtr, end_lsn)) {
record = XLogReadRecord(xlogreader_state, first_record, &errormsg);
valid_end_lsn = xlogreader_state->EndRecPtr;
if (!record && XLByteLT(valid_end_lsn, end_lsn)) {
/* if we are recycling or removing log files concurrently, and we can't find the next record right after.
* In this case, we try to read from the current oldest xlog file. */
// 如果无法读取下一条记录则更新min_lsn并继续尝试读取
bool found = false;
XLogRecPtr temp_start_lsn = Max(xlogreader_state->EndRecPtr, start_lsn);
first_record = UpdateNextLSN(temp_start_lsn, end_lsn, xlogreader_state, &found);
if (found) {
ereport(WARNING, (errcode(ERRCODE_WARNING),
(errmsg("We cannot read %X/%X. After retried, we jump to read the next available %X/%X. " \
"The missing part might be recycled or removed.",
(uint32)(temp_start_lsn >> XIDTHIRTYTWO), (uint32)temp_start_lsn,
(uint32)(first_record >> XIDTHIRTYTWO), (uint32)first_record))));
continue;
}
if (errormsg != NULL)
ereport(LOG,
(errcode(ERRCODE_LOG),
(errmsg("could not read WAL record at %X/%X: %s",
(uint32)(xlogreader_state->ReadRecPtr >> XIDTHIRTYTWO),
(uint32)xlogreader_state->ReadRecPtr,
errormsg))));
else
ereport(LOG,
(errcode(ERRCODE_LOG),
(errmsg("could not read WAL record at %X/%X",
(uint32)(xlogreader_state->ReadRecPtr >> XIDTHIRTYTWO),
(uint32)xlogreader_state->ReadRecPtr))));
break;
}
first_record = InvalidXLogRecPtr; /* No explicit start point; read the record after the one we just read */
// 根据过滤器筛选记录,并输出
if (filter->by_xid_enabled && filter->by_xid != record->xl_xid) {
continue;
}
if (filter->by_tablepath_enabled) { /* filter by table path */
if (!XLogRecHasAnyBlockRefs(xlogreader_state)) {
continue;
} else if (!CheckValidRecord(xlogreader_state, filter)) {
/* at least have one block ref, but not match filter */
continue;
}
}
strOutput = (char *)palloc(MAXOUTPUTLEN * sizeof(char));
rc = memset_s(strOutput, MAXOUTPUTLEN, 0, MAXOUTPUTLEN);
securec_check(rc, "\0", "\0");
XLogDumpDisplayRecord(xlogreader_state, strOutput);
count++;
CheckWriteFile(fwrite(strOutput, 1, strlen(strOutput), outputfile), strlen(strOutput), outputFilename);
pfree_ext(strOutput);
}
// 输出结束信息并关闭文件
XLogReaderFree(xlogreader_state);
strOutput = (char *)palloc(MAXOUTPUTLEN * sizeof(char));
rc = memset_s(strOutput, MAXOUTPUTLEN, 0, MAXOUTPUTLEN);
securec_check(rc, "\0", "\0");
/* Summary(xx total): valid start_lsn: xxx, valid end_lsn: xxx */
rc = snprintf_s(strOutput + (int)strlen(strOutput), MAXOUTPUTLEN, MAXOUTPUTLEN - 1,
"\nSummary (%d total): valid start_lsn: %X/%X, valid end_lsn: %X/%X\n", count,
(uint32)(valid_start_lsn >> XIDTHIRTYTWO), (uint32)(valid_start_lsn), (uint32)(valid_end_lsn >> XIDTHIRTYTWO),
(uint32)(valid_end_lsn));
securec_check_ss(rc, "\0", "\0");
/* generate output file */
CheckWriteFile(fwrite(strOutput, 1, strlen(strOutput), outputfile), strlen(strOutput), outputFilename);
CheckCloseFile(fclose(outputfile), outputFilename);
pfree_ext(strOutput);
CloseXlogFile();
}
/* There are only two parameters in PG_FUNCTION_ARGS: start_lsn and end_lsn */
/*
这是一个PostgreSQL函数"gs_xlogdump_lsn"。
它接收起始LSN和结束LSN作为输入参数并对指定的WAL文件进行转储。
*/
Datum gs_xlogdump_lsn(PG_FUNCTION_ARGS)
{
errno_t rc = EOK;
/* check user's right */
/* 检查用户权限 */
const char fName[MAXFNAMELEN] = "gs_xlogdump_lsn";
CheckUser(fName);
/* read in parameters */
/* 读取并验证起始LSN和结束LSN */
char *start_lsn_str = text_to_cstring(PG_GETARG_TEXT_P(0));
char *end_lsn_str = text_to_cstring(PG_GETARG_TEXT_P(1));
/* validate lsn */
XLogRecPtr start_lsn, end_lsn;
ValidateStartEndLSN(start_lsn_str, end_lsn_str, &start_lsn, &end_lsn);
/*
根据起始LSN和结束LSN生成输出文件名
这里使用了GenerateOutputFileName函数
*/
char *outputFilename = (char *)palloc(MAXFILENAME * sizeof(char));
rc = memset_s(outputFilename, MAXFILENAME, 0, MAXFILENAME);
securec_check(rc, "\0", "\0");
GenerateOutputFileName(outputFilename, start_lsn_str, end_lsn_str);
/* update start_lsn and end_lsn based on cur min_lsn and max_lsn */
/*
根据当前系统中的min_lsn和max_lsn更新起始LSN和结束LSN
这是为了避免转储过程中出现不一致的情况。
*/
XLogRecPtr min_lsn = GetMinLSN();
XLogRecPtr max_lsn = GetMaxLSN();
if (XLByteLT(start_lsn, min_lsn)) {
start_lsn = min_lsn;
}
if (XLByteLT(max_lsn, end_lsn)) {
end_lsn = max_lsn;
}
/* 初始化一个XLogFilter结构体用于筛选日志记录 */
XLogFilter filter;
rc = memset_s(&filter, sizeof(XLogFilter), 0, sizeof(XLogFilter));
securec_check_ss(rc, "\0", "\0");
/* 对指定的WAL文件进行转储 */
XLogDump(start_lsn, end_lsn, &filter, outputFilename);
/* 返回输出文件名 */
PG_RETURN_TEXT_P(cstring_to_text(outputFilename));
}
/* There are only one parameter in PG_FUNCTION_ARGS: c_xid */
//基于事务ID对WAL文件进行转储
Datum gs_xlogdump_xid(PG_FUNCTION_ARGS)
{
errno_t rc = EOK;
/* check user's right */
/* 检查用户是否有权限调用该函数 */
const char fName[MAXFNAMELEN] = "gs_xlogdump_xid";
CheckUser(fName);
/* read in parameters */
/* 读取并验证传入的事务ID */
TransactionId c_xid = PG_GETARG_TRANSACTIONID(0);
/* check parameters */
/* 获取系统当前的最大事务ID以确保传入的事务ID有效 */
TransactionId topXid = GetTopTransactionId();
if (TransactionIdPrecedes(topXid, c_xid))
ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
(errmsg("Xid should less than or equal to %lu.", topXid))));
/* generate output file name */
/* 生成输出文件名 */
char *outputFilename = (char *)palloc(MAXFILENAME * sizeof(char));
rc = memset_s(outputFilename, MAXFILENAME, 0, MAXFILENAME);
securec_check(rc, "\0", "\0");
rc = snprintf_s(outputFilename + (int)strlen(outputFilename), MAXFILENAME, MAXFILENAME - 1, "%s/%lu.xlog",
t_thrd.proc_cxt.DataDir, c_xid);
securec_check_ss(rc, "\0", "\0");
/* update start_lsn and end_lsn based on cur min_lsn and max_lsn */
/* 获取最小LSN和最大LSN并更新转储范围 */
XLogRecPtr min_lsn = GetMinLSN();
XLogRecPtr max_lsn = GetMaxLSN();
/* 初始化XLogFilter结构体设定需要根据事务ID来筛选记录 */
XLogFilter filter;
rc = memset_s(&filter, sizeof(XLogFilter), 0, sizeof(XLogFilter));
securec_check(rc, "\0", "\0");
filter.by_xid_enabled = true;
filter.by_xid = c_xid;
/* 调用XLogDump函数进行转储 */
XLogDump(min_lsn, max_lsn, &filter, outputFilename);
/* 返回输出文件名 */
PG_RETURN_TEXT_P(cstring_to_text(outputFilename));
}
//用于将指定路径和块号(或所有块)下的 WAL 日志记录转储到输出文件中
/* There are only three parameters in PG_FUNCTION_ARGS: path, blocknum, relation_type */
Datum gs_xlogdump_tablepath(PG_FUNCTION_ARGS)
{
errno_t rc = EOK;
/* check user's right */
//查用户是否有执行该函数的权限
const char fName[MAXFNAMELEN] = "gs_xlogdump_tablepath";
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));
/* check parameters */
//检查块号是否在合法范围内(-1 到 MaxBlockNumber 之间),如果不在范围内,则抛出错误
if (blocknum > MaxBlockNumber || blocknum < -1)
ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
(errmsg("Blocknum should be between -1 and %u.", MaxBlockNumber))));
/* update start_lsn and end_lsn based on cur min_lsn and max_lsn */
XLogRecPtr min_lsn = GetMinLSN();
XLogRecPtr max_lsn = GetMaxLSN();
XLogFilter filter;
rc = memset_s(&filter, sizeof(XLogFilter), 0, sizeof(XLogFilter));
securec_check_c(rc, "\0", "\0");
filter.by_tablepath_enabled = true;
if (blocknum == -1) { /* care all blocks */ //示处理所有块
filter.by_block = false;
} else { /* only one block */
filter.by_block = true;
filter.blocknum = blocknum;
}
/* generate output file name */
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, &(filter.by_relfilenode), false);//根据 filter.by_relfilenode验证路径和关联关系类型参数的合法性
ValidateParameterPath(filter.by_relfilenode, path);
//执行转储操作
XLogDump(min_lsn, max_lsn, &filter, outputFilename);
PG_RETURN_TEXT_P(cstring_to_text(outputFilename));//将输出文件名转换为 PostgreSQL 文本类型
}
//解析和转储数据库中的WALWrite-Ahead Log日志记录和数据页
Datum gs_xlogdump_parsepage_tablepath(PG_FUNCTION_ARGS)
{
/* check user's right */
//检查用户权限
const char fName[MAXFNAMELEN] = "gs_xlogdump_parsepage_tablepath";
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);
int rc = -1;
/* page part, copy path since for segment we will edit path */
//读取传入的函数参数,并进行相应的数据类型转换
char *path_cpy = (char *)palloc(MAXFILENAME + 1);
rc = strcpy_s(path_cpy, MAXFILENAME + 1, path);
securec_check(rc, "\0", "\0");
/* 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 *outputFilenamePage = ParsePage(path_cpy, blocknum, relation_type, read_memory);
pfree_ext(path_cpy);
/* xlog part */
/* update start_lsn and end_lsn based on cur min_lsn and max_lsn */
XLogFilter filter;
rc = memset_s(&filter, sizeof(XLogFilter), 0, sizeof(XLogFilter));
securec_check_c(rc, "\0", "\0");
if (blocknum == -1) { /* care all blocks */
filter.by_block = false;
} else { /* only one block */
filter.by_block = true;
filter.blocknum = blocknum;
}
filter.by_tablepath_enabled = true;
/* generate output file name */
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, &(filter.by_relfilenode), false);
//获取最小和最大 LSN 并调用 XLogDump 函数将符合筛选条件的 WAL 日志记录转储到输出文件中。
XLogRecPtr min_lsn = GetMinLSN();
XLogRecPtr max_lsn = GetMaxLSN();
XLogDump(min_lsn, max_lsn, &filter, outputFilename);
//计算生成结果字符串所需的长度,并为结果字符串分配内存
int outputLen = strlen(outputFilename) + strlen(outputFilenamePage) + 100;
if (outputLen <= 0)
ereport(ERROR, (errcode(ERRCODE_IO_ERROR), (errmsg("Cannot generate right output file name."))));
char *result = (char *)palloc(outputLen * sizeof(char));
rc = memset_s(result, outputLen, 0, outputLen);
securec_check(rc, "\0", "\0");
rc = snprintf_s(result + (int)strlen(result), outputLen, outputLen - 1,
"Output file for parsing xlog: %s\nOutput file for parsing data page: %s", outputFilename, outputFilenamePage);
securec_check_ss(rc, "\0", "\0");
pfree_ext(outputFilename);
PG_RETURN_TEXT_P(cstring_to_text(result));
}