1
0
mirror of https://github.com/postgres/postgres.git synced 2025-06-29 10:41:53 +03:00

Add support for forcing a switch to a new xlog file; cause such a switch

to happen automatically during pg_stop_backup().  Add some functions for
interrogating the current xlog insertion point and for easily extracting
WAL filenames from the hex WAL locations displayed by pg_stop_backup
and friends.  Simon Riggs with some editorialization by Tom Lane.
This commit is contained in:
Tom Lane
2006-08-06 03:53:44 +00:00
parent c0dc166adc
commit 704ddaaa09
7 changed files with 516 additions and 67 deletions

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/backend/access/transam/xlog.c,v 1.245 2006/07/30 02:07:18 alvherre Exp $
* $PostgreSQL: pgsql/src/backend/access/transam/xlog.c,v 1.246 2006/08/06 03:53:44 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -312,7 +312,8 @@ static XLogRecPtr RedoRecPtr;
* CheckpointLock: must be held to do a checkpoint (ensures only one
* checkpointer at a time; even though the postmaster won't launch
* parallel checkpoint processes, we need this because manual checkpoints
* could be launched simultaneously).
* could be launched simultaneously). XXX now that all checkpoints are
* done by the bgwriter, isn't this lock redundant?
*
*----------
*/
@ -465,8 +466,8 @@ static bool recoveryStopsHere(XLogRecord *record, bool *includeThis);
static bool XLogCheckBuffer(XLogRecData *rdata, bool doPageWrites,
XLogRecPtr *lsn, BkpBlock *bkpb);
static bool AdvanceXLInsertBuffer(void);
static void XLogWrite(XLogwrtRqst WriteRqst, bool flexible);
static bool AdvanceXLInsertBuffer(bool new_segment);
static void XLogWrite(XLogwrtRqst WriteRqst, bool flexible, bool xlog_switch);
static int XLogFileInit(uint32 log, uint32 seg,
bool *use_existent, bool use_lock);
static bool InstallXLogFileSegment(uint32 *log, uint32 *seg, char *tmppath,
@ -543,7 +544,8 @@ XLogInsert(RmgrId rmid, uint8 info, XLogRecData *rdata)
XLogwrtRqst LogwrtRqst;
bool updrqst;
bool doPageWrites;
bool no_tran = (rmid == RM_XLOG_ID) ? true : false;
bool isLogSwitch = (rmid == RM_XLOG_ID && info == XLOG_SWITCH);
bool no_tran = (rmid == RM_XLOG_ID);
if (info & XLR_INFO_MASK)
{
@ -687,14 +689,13 @@ begin:;
}
/*
* NOTE: the test for len == 0 here is somewhat fishy, since in theory all
* of the rmgr data might have been suppressed in favor of backup blocks.
* Currently, all callers of XLogInsert provide at least some
* not-in-a-buffer data and so len == 0 should never happen, but that may
* not be true forever. If you need to remove the len == 0 check, also
* remove the check for xl_len == 0 in ReadRecord, below.
* NOTE: We disallow len == 0 because it provides a useful bit of extra
* error checking in ReadRecord. This means that all callers of
* XLogInsert must supply at least some not-in-a-buffer data. However,
* we make an exception for XLOG SWITCH records because we don't want
* them to ever cross a segment boundary.
*/
if (len == 0)
if (len == 0 && !isLogSwitch)
elog(PANIC, "invalid xlog record length %u", len);
START_CRIT_SECTION();
@ -731,7 +732,7 @@ begin:;
*/
LogwrtResult = XLogCtl->Write.LogwrtResult;
if (XLByteLT(LogwrtResult.Write, LogwrtRqst.Write))
XLogWrite(LogwrtRqst, true);
XLogWrite(LogwrtRqst, true, false);
LWLockRelease(WALWriteLock);
}
}
@ -854,15 +855,55 @@ begin:;
freespace = INSERT_FREESPACE(Insert);
if (freespace < SizeOfXLogRecord)
{
updrqst = AdvanceXLInsertBuffer();
updrqst = AdvanceXLInsertBuffer(false);
freespace = INSERT_FREESPACE(Insert);
}
/* Compute record's XLOG location */
curridx = Insert->curridx;
record = (XLogRecord *) Insert->currpos;
INSERT_RECPTR(RecPtr, Insert, curridx);
/*
* If the record is an XLOG_SWITCH, and we are exactly at the start
* of a segment, we need not insert it (and don't want to because
* we'd like consecutive switch requests to be no-ops). Instead,
* make sure everything is written and flushed through the end of
* the prior segment, and return the prior segment's end address.
*/
if (isLogSwitch &&
(RecPtr.xrecoff % XLogSegSize) == SizeOfXLogLongPHD)
{
/* We can release insert lock immediately */
LWLockRelease(WALInsertLock);
RecPtr.xrecoff -= SizeOfXLogLongPHD;
if (RecPtr.xrecoff == 0)
{
/* crossing a logid boundary */
RecPtr.xlogid -= 1;
RecPtr.xrecoff = XLogFileSize;
}
LWLockAcquire(WALWriteLock, LW_EXCLUSIVE);
LogwrtResult = XLogCtl->Write.LogwrtResult;
if (!XLByteLE(RecPtr, LogwrtResult.Flush))
{
XLogwrtRqst FlushRqst;
FlushRqst.Write = RecPtr;
FlushRqst.Flush = RecPtr;
XLogWrite(FlushRqst, false, false);
}
LWLockRelease(WALWriteLock);
END_CRIT_SECTION();
return RecPtr;
}
/* Insert record header */
record = (XLogRecord *) Insert->currpos;
record->xl_prev = Insert->PrevRecord;
record->xl_xid = GetCurrentTransactionIdIfAny();
record->xl_tot_len = SizeOfXLogRecord + write_len;
@ -876,17 +917,14 @@ begin:;
FIN_CRC32(rdata_crc);
record->xl_crc = rdata_crc;
/* Compute record's XLOG location */
INSERT_RECPTR(RecPtr, Insert, curridx);
#ifdef WAL_DEBUG
if (XLOG_DEBUG)
{
StringInfoData buf;
initStringInfo(&buf);
appendStringInfo(&buf, "INSERT @ %X/%X: ",
RecPtr.xlogid, RecPtr.xrecoff);
appendStringInfo(&buf, "INSERT @ %X/%X: ",
RecPtr.xlogid, RecPtr.xrecoff);
xlog_outrec(&buf, record);
if (rdata->data != NULL)
{
@ -937,7 +975,7 @@ begin:;
}
/* Use next buffer */
updrqst = AdvanceXLInsertBuffer();
updrqst = AdvanceXLInsertBuffer(false);
curridx = Insert->curridx;
/* Insert cont-record header */
Insert->currpage->xlp_info |= XLP_FIRST_IS_CONTRECORD;
@ -958,13 +996,92 @@ begin:;
*/
INSERT_RECPTR(RecPtr, Insert, curridx);
/* Need to update shared LogwrtRqst if some block was filled up */
if (freespace < SizeOfXLogRecord)
updrqst = true; /* curridx is filled and available for writing
* out */
/*
* If the record is an XLOG_SWITCH, we must now write and flush all the
* existing data, and then forcibly advance to the start of the next
* segment. It's not good to do this I/O while holding the insert lock,
* but there seems too much risk of confusion if we try to release the
* lock sooner. Fortunately xlog switch needn't be a high-performance
* operation anyway...
*/
if (isLogSwitch)
{
XLogCtlWrite *Write = &XLogCtl->Write;
XLogwrtRqst FlushRqst;
XLogRecPtr OldSegEnd;
LWLockAcquire(WALWriteLock, LW_EXCLUSIVE);
/*
* Flush through the end of the page containing XLOG_SWITCH,
* and perform end-of-segment actions (eg, notifying archiver).
*/
WriteRqst = XLogCtl->xlblocks[curridx];
FlushRqst.Write = WriteRqst;
FlushRqst.Flush = WriteRqst;
XLogWrite(FlushRqst, false, true);
/* Set up the next buffer as first page of next segment */
/* Note: AdvanceXLInsertBuffer cannot need to do I/O here */
(void) AdvanceXLInsertBuffer(true);
/* There should be no unwritten data */
curridx = Insert->curridx;
Assert(curridx == Write->curridx);
/* Compute end address of old segment */
OldSegEnd = XLogCtl->xlblocks[curridx];
OldSegEnd.xrecoff -= XLOG_BLCKSZ;
if (OldSegEnd.xrecoff == 0)
{
/* crossing a logid boundary */
OldSegEnd.xlogid -= 1;
OldSegEnd.xrecoff = XLogFileSize;
}
/* Make it look like we've written and synced all of old segment */
LogwrtResult.Write = OldSegEnd;
LogwrtResult.Flush = OldSegEnd;
/*
* Update shared-memory status --- this code should match XLogWrite
*/
{
/* use volatile pointer to prevent code rearrangement */
volatile XLogCtlData *xlogctl = XLogCtl;
SpinLockAcquire(&xlogctl->info_lck);
xlogctl->LogwrtResult = LogwrtResult;
if (XLByteLT(xlogctl->LogwrtRqst.Write, LogwrtResult.Write))
xlogctl->LogwrtRqst.Write = LogwrtResult.Write;
if (XLByteLT(xlogctl->LogwrtRqst.Flush, LogwrtResult.Flush))
xlogctl->LogwrtRqst.Flush = LogwrtResult.Flush;
SpinLockRelease(&xlogctl->info_lck);
}
Write->LogwrtResult = LogwrtResult;
LWLockRelease(WALWriteLock);
updrqst = false; /* done already */
}
else
curridx = PrevBufIdx(curridx);
WriteRqst = XLogCtl->xlblocks[curridx];
{
/* normal case, ie not xlog switch */
/* Need to update shared LogwrtRqst if some block was filled up */
if (freespace < SizeOfXLogRecord)
{
/* curridx is filled and available for writing out */
updrqst = true;
}
else
{
/* if updrqst already set, write through end of previous buf */
curridx = PrevBufIdx(curridx);
}
WriteRqst = XLogCtl->xlblocks[curridx];
}
LWLockRelease(WALInsertLock);
@ -1173,6 +1290,10 @@ XLogArchiveCleanup(const char *xlog)
* Advance the Insert state to the next buffer page, writing out the next
* buffer if it still contains unwritten data.
*
* If new_segment is TRUE then we set up the next buffer page as the first
* page of the next xlog segment file, possibly but not usually the next
* consecutive file page.
*
* The global LogwrtRqst.Write pointer needs to be advanced to include the
* just-filled page. If we can do this for free (without an extra lock),
* we do so here. Otherwise the caller must do it. We return TRUE if the
@ -1181,7 +1302,7 @@ XLogArchiveCleanup(const char *xlog)
* Must be called with WALInsertLock held.
*/
static bool
AdvanceXLInsertBuffer(void)
AdvanceXLInsertBuffer(bool new_segment)
{
XLogCtlInsert *Insert = &XLogCtl->Insert;
XLogCtlWrite *Write = &XLogCtl->Write;
@ -1248,7 +1369,7 @@ AdvanceXLInsertBuffer(void)
WriteRqst.Write = OldPageRqstPtr;
WriteRqst.Flush.xlogid = 0;
WriteRqst.Flush.xrecoff = 0;
XLogWrite(WriteRqst, false);
XLogWrite(WriteRqst, false, false);
LWLockRelease(WALWriteLock);
Insert->LogwrtResult = LogwrtResult;
}
@ -1260,6 +1381,14 @@ AdvanceXLInsertBuffer(void)
* output page.
*/
NewPageEndPtr = XLogCtl->xlblocks[Insert->curridx];
if (new_segment)
{
/* force it to a segment start point */
NewPageEndPtr.xrecoff += XLogSegSize - 1;
NewPageEndPtr.xrecoff -= NewPageEndPtr.xrecoff % XLogSegSize;
}
if (NewPageEndPtr.xrecoff >= XLogFileSize)
{
/* crossing a logid boundary */
@ -1318,13 +1447,20 @@ AdvanceXLInsertBuffer(void)
* This option allows us to avoid uselessly issuing multiple writes when a
* single one would do.
*
* If xlog_switch == TRUE, we are intending an xlog segment switch, so
* perform end-of-segment actions after writing the last page, even if
* it's not physically the end of its segment. (NB: this will work properly
* only if caller specifies WriteRqst == page-end and flexible == false,
* and there is some data to write.)
*
* Must be called with WALWriteLock held.
*/
static void
XLogWrite(XLogwrtRqst WriteRqst, bool flexible)
XLogWrite(XLogwrtRqst WriteRqst, bool flexible, bool xlog_switch)
{
XLogCtlWrite *Write = &XLogCtl->Write;
bool ispartialpage;
bool last_iteration;
bool finishing_seg;
bool use_existent;
int curridx;
@ -1468,10 +1604,12 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible)
* contiguous in memory), or if we are at the end of the logfile
* segment.
*/
last_iteration = !XLByteLT(LogwrtResult.Write, WriteRqst.Write);
finishing_seg = !ispartialpage &&
(startoffset + npages * XLOG_BLCKSZ) >= XLogSegSize;
if (!XLByteLT(LogwrtResult.Write, WriteRqst.Write) ||
if (last_iteration ||
curridx == XLogCtl->XLogCacheBlck ||
finishing_seg)
{
@ -1519,10 +1657,13 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible)
* later. Doing it here ensures that one and only one backend will
* perform this fsync.
*
* We also do this if this is the last page written for an xlog
* switch.
*
* This is also the right place to notify the Archiver that the
* segment is ready to copy to archival storage.
*/
if (finishing_seg)
if (finishing_seg || (xlog_switch && last_iteration))
{
issue_xlog_fsync();
LogwrtResult.Flush = LogwrtResult.Write; /* end of page */
@ -1681,7 +1822,7 @@ XLogFlush(XLogRecPtr record)
WriteRqst.Write = WriteRqstPtr;
WriteRqst.Flush = record;
}
XLogWrite(WriteRqst, false);
XLogWrite(WriteRqst, false, false);
}
LWLockRelease(WALWriteLock);
}
@ -2828,10 +2969,20 @@ ReadRecord(XLogRecPtr *RecPtr, int emode)
got_record:;
/*
* Currently, xl_len == 0 must be bad data, but that might not be true
* forever. See note in XLogInsert.
* xl_len == 0 is bad data for everything except XLOG SWITCH, where
* it is required.
*/
if (record->xl_len == 0)
if (record->xl_rmid == RM_XLOG_ID && record->xl_info == XLOG_SWITCH)
{
if (record->xl_len != 0)
{
ereport(emode,
(errmsg("invalid xlog switch record at %X/%X",
RecPtr->xlogid, RecPtr->xrecoff)));
goto next_record_is_invalid;
}
}
else if (record->xl_len == 0)
{
ereport(emode,
(errmsg("record with zero length at %X/%X",
@ -2994,6 +3145,7 @@ got_record:;
pageHeaderSize +
MAXALIGN(SizeOfXLogContRecord + contrecord->xl_rem_len);
ReadRecPtr = *RecPtr;
/* needn't worry about XLOG SWITCH, it can't cross page boundaries */
return record;
}
@ -3007,6 +3159,22 @@ got_record:;
EndRecPtr.xrecoff = RecPtr->xrecoff + MAXALIGN(total_len);
ReadRecPtr = *RecPtr;
memcpy(buffer, record, total_len);
/*
* Special processing if it's an XLOG SWITCH record
*/
if (record->xl_rmid == RM_XLOG_ID && record->xl_info == XLOG_SWITCH)
{
/* Pretend it extends to end of segment */
EndRecPtr.xrecoff += XLogSegSize - 1;
EndRecPtr.xrecoff -= EndRecPtr.xrecoff % XLogSegSize;
nextRecord = NULL; /* definitely not on same page */
/*
* Pretend that readBuf contains the last page of the segment.
* This is just to avoid Assert failure in StartupXLOG if XLOG
* ends with this segment.
*/
readOff = XLogSegSize - XLOG_BLCKSZ;
}
return (XLogRecord *) buffer;
next_record_is_invalid:;
@ -5262,7 +5430,7 @@ CreateCheckPoint(bool shutdown, bool force)
freespace = INSERT_FREESPACE(Insert);
if (freespace < SizeOfXLogRecord)
{
(void) AdvanceXLInsertBuffer();
(void) AdvanceXLInsertBuffer(false);
/* OK to ignore update return flag, since we will do flush anyway */
freespace = INSERT_FREESPACE(Insert);
}
@ -5447,6 +5615,33 @@ XLogPutNextOid(Oid nextOid)
*/
}
/*
* Write an XLOG SWITCH record.
*
* Here we just blindly issue an XLogInsert request for the record.
* All the magic happens inside XLogInsert.
*
* The return value is either the end+1 address of the switch record,
* or the end+1 address of the prior segment if we did not need to
* write a switch record because we are already at segment start.
*/
static XLogRecPtr
RequestXLogSwitch(void)
{
XLogRecPtr RecPtr;
XLogRecData rdata;
/* XLOG SWITCH, alone among xlog record types, has no data */
rdata.buffer = InvalidBuffer;
rdata.data = NULL;
rdata.len = 0;
rdata.next = NULL;
RecPtr = XLogInsert(RM_XLOG_ID, XLOG_SWITCH, &rdata);
return RecPtr;
}
/*
* XLOG resource manager's routines
*/
@ -5515,6 +5710,10 @@ xlog_redo(XLogRecPtr lsn, XLogRecord *record)
(errmsg("unexpected timeline ID %u (should be %u) in checkpoint record",
checkPoint.ThisTimeLineID, ThisTimeLineID)));
}
else if (info == XLOG_SWITCH)
{
/* nothing to do here */
}
}
void
@ -5544,6 +5743,10 @@ xlog_desc(StringInfo buf, uint8 xl_info, char *rec)
memcpy(&nextOid, rec, sizeof(Oid));
appendStringInfo(buf, "nextOid: %u", nextOid);
}
else if (info == XLOG_SWITCH)
{
appendStringInfo(buf, "xlog switch");
}
else
appendStringInfo(buf, "UNKNOWN");
}
@ -5694,7 +5897,7 @@ issue_xlog_fsync(void)
* where it will be archived as part of the backup dump. The label file
* contains the user-supplied label string (typically this would be used
* to tell where the backup dump will be stored) and the starting time and
* starting WAL offset for the dump.
* starting WAL location for the dump.
*/
Datum
pg_start_backup(PG_FUNCTION_ARGS)
@ -5844,7 +6047,7 @@ pg_start_backup(PG_FUNCTION_ARGS)
PG_END_TRY();
/*
* We're done. As a convenience, return the starting WAL offset.
* We're done. As a convenience, return the starting WAL location.
*/
snprintf(xlogfilename, sizeof(xlogfilename), "%X/%X",
startpoint.xlogid, startpoint.xrecoff);
@ -5859,13 +6062,12 @@ pg_start_backup(PG_FUNCTION_ARGS)
* We remove the backup label file created by pg_start_backup, and instead
* create a backup history file in pg_xlog (whence it will immediately be
* archived). The backup history file contains the same info found in
* the label file, plus the backup-end time and WAL offset.
* the label file, plus the backup-end time and WAL location.
*/
Datum
pg_stop_backup(PG_FUNCTION_ARGS)
{
text *result;
XLogCtlInsert *Insert = &XLogCtl->Insert;
XLogRecPtr startpoint;
XLogRecPtr stoppoint;
time_t stamp_time;
@ -5886,15 +6088,20 @@ pg_stop_backup(PG_FUNCTION_ARGS)
(errmsg("must be superuser to run a backup"))));
/*
* Get the current end-of-WAL position; it will be unsafe to use this dump
* to restore to a point in advance of this time. We can also clear
* forcePageWrites here.
* OK to clear forcePageWrites
*/
LWLockAcquire(WALInsertLock, LW_EXCLUSIVE);
INSERT_RECPTR(stoppoint, Insert, Insert->curridx);
XLogCtl->Insert.forcePageWrites = false;
LWLockRelease(WALInsertLock);
/*
* Force a switch to a new xlog segment file, so that the backup
* is valid as soon as archiver moves out the current segment file.
* We'll report the end address of the XLOG SWITCH record as the backup
* stopping point.
*/
stoppoint = RequestXLogSwitch();
XLByteToSeg(stoppoint, _logId, _logSeg);
XLogFileName(stopxlogfilename, ThisTimeLineID, _logId, _logSeg);
@ -5984,7 +6191,7 @@ pg_stop_backup(PG_FUNCTION_ARGS)
CleanupBackupHistory();
/*
* We're done. As a convenience, return the ending WAL offset.
* We're done. As a convenience, return the ending WAL location.
*/
snprintf(stopxlogfilename, sizeof(stopxlogfilename), "%X/%X",
stoppoint.xlogid, stoppoint.xrecoff);
@ -5993,6 +6200,144 @@ pg_stop_backup(PG_FUNCTION_ARGS)
PG_RETURN_TEXT_P(result);
}
/*
* pg_switch_xlog: switch to next xlog file
*/
Datum
pg_switch_xlog(PG_FUNCTION_ARGS)
{
text *result;
XLogRecPtr switchpoint;
char location[MAXFNAMELEN];
if (!superuser())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
(errmsg("must be superuser to switch xlog files"))));
switchpoint = RequestXLogSwitch();
/*
* As a convenience, return the WAL location of the switch record
*/
snprintf(location, sizeof(location), "%X/%X",
switchpoint.xlogid, switchpoint.xrecoff);
result = DatumGetTextP(DirectFunctionCall1(textin,
CStringGetDatum(location)));
PG_RETURN_TEXT_P(result);
}
/*
* Report the current WAL location (same format as pg_start_backup etc)
*/
Datum
pg_current_xlog_location(PG_FUNCTION_ARGS)
{
text *result;
XLogCtlInsert *Insert = &XLogCtl->Insert;
XLogRecPtr current_recptr;
char location[MAXFNAMELEN];
/*
* Get the current end-of-WAL position ... shared lock is sufficient
*/
LWLockAcquire(WALInsertLock, LW_SHARED);
INSERT_RECPTR(current_recptr, Insert, Insert->curridx);
LWLockRelease(WALInsertLock);
snprintf(location, sizeof(location), "%X/%X",
current_recptr.xlogid, current_recptr.xrecoff);
result = DatumGetTextP(DirectFunctionCall1(textin,
CStringGetDatum(location)));
PG_RETURN_TEXT_P(result);
}
/*
* Compute an xlog file name and decimal byte offset given a WAL location,
* such as is returned by pg_stop_backup() or pg_xlog_switch().
*
* Note that a location exactly at a segment boundary is taken to be in
* the previous segment. This is usually the right thing, since the
* expected usage is to determine which xlog file(s) are ready to archive.
*/
Datum
pg_xlogfile_name_offset(PG_FUNCTION_ARGS)
{
text *location = PG_GETARG_TEXT_P(0);
text *result;
char *locationstr;
unsigned int uxlogid;
unsigned int uxrecoff;
uint32 xlogid;
uint32 xlogseg;
uint32 xrecoff;
XLogRecPtr locationpoint;
char xlogfilename[MAXFNAMELEN];
locationstr = DatumGetCString(DirectFunctionCall1(textout,
PointerGetDatum(location)));
if (sscanf(locationstr, "%X/%X", &uxlogid, &uxrecoff) != 2)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("could not parse xlog location \"%s\"",
locationstr)));
locationpoint.xlogid = uxlogid;
locationpoint.xrecoff = uxrecoff;
XLByteToPrevSeg(locationpoint, xlogid, xlogseg);
XLogFileName(xlogfilename, ThisTimeLineID, xlogid, xlogseg);
xrecoff = locationpoint.xrecoff - xlogseg * XLogSegSize;
snprintf(xlogfilename + strlen(xlogfilename),
sizeof(xlogfilename) - strlen(xlogfilename),
" %u",
(unsigned int) xrecoff);
result = DatumGetTextP(DirectFunctionCall1(textin,
CStringGetDatum(xlogfilename)));
PG_RETURN_TEXT_P(result);
}
/*
* Compute an xlog file name given a WAL location,
* such as is returned by pg_stop_backup() or pg_xlog_switch().
*/
Datum
pg_xlogfile_name(PG_FUNCTION_ARGS)
{
text *location = PG_GETARG_TEXT_P(0);
text *result;
char *locationstr;
unsigned int uxlogid;
unsigned int uxrecoff;
uint32 xlogid;
uint32 xlogseg;
XLogRecPtr locationpoint;
char xlogfilename[MAXFNAMELEN];
locationstr = DatumGetCString(DirectFunctionCall1(textout,
PointerGetDatum(location)));
if (sscanf(locationstr, "%X/%X", &uxlogid, &uxrecoff) != 2)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("could not parse xlog location \"%s\"",
locationstr)));
locationpoint.xlogid = uxlogid;
locationpoint.xrecoff = uxrecoff;
XLByteToPrevSeg(locationpoint, xlogid, xlogseg);
XLogFileName(xlogfilename, ThisTimeLineID, xlogid, xlogseg);
result = DatumGetTextP(DirectFunctionCall1(textin,
CStringGetDatum(xlogfilename)));
PG_RETURN_TEXT_P(result);
}
/*
* read_backup_label: check to see if a backup_label file is present
*
@ -6133,8 +6478,8 @@ rm_redo_error_callback(void *arg)
StringInfoData buf;
initStringInfo(&buf);
RmgrTable[record->xl_rmid].rm_desc(&buf,
record->xl_info,
RmgrTable[record->xl_rmid].rm_desc(&buf,
record->xl_info,
XLogRecGetData(record));
/* don't bother emitting empty description */