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:
@ -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 */
|
||||
|
Reference in New Issue
Block a user