Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * xlogrecovery.c
4 : : * Functions for WAL recovery, standby mode
5 : : *
6 : : * This source file contains functions controlling WAL recovery.
7 : : * InitWalRecovery() initializes the system for crash or archive recovery,
8 : : * or standby mode, depending on configuration options and the state of
9 : : * the control file and possible backup label file. PerformWalRecovery()
10 : : * performs the actual WAL replay, calling the rmgr-specific redo routines.
11 : : * FinishWalRecovery() performs end-of-recovery checks and cleanup actions,
12 : : * and prepares information needed to initialize the WAL for writes. In
13 : : * addition to these three main functions, there are a bunch of functions
14 : : * for interrogating recovery state and controlling the recovery process.
15 : : *
16 : : *
17 : : * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
18 : : * Portions Copyright (c) 1994, Regents of the University of California
19 : : *
20 : : * src/backend/access/transam/xlogrecovery.c
21 : : *
22 : : *-------------------------------------------------------------------------
23 : : */
24 : :
25 : : #include "postgres.h"
26 : :
27 : : #include <ctype.h>
28 : : #include <time.h>
29 : : #include <sys/stat.h>
30 : : #include <sys/time.h>
31 : : #include <unistd.h>
32 : :
33 : : #include "access/timeline.h"
34 : : #include "access/transam.h"
35 : : #include "access/xact.h"
36 : : #include "access/xlog_internal.h"
37 : : #include "access/xlogarchive.h"
38 : : #include "access/xlogprefetcher.h"
39 : : #include "access/xlogreader.h"
40 : : #include "access/xlogrecovery.h"
41 : : #include "access/xlogutils.h"
42 : : #include "access/xlogwait.h"
43 : : #include "backup/basebackup.h"
44 : : #include "catalog/pg_control.h"
45 : : #include "commands/tablespace.h"
46 : : #include "common/file_utils.h"
47 : : #include "common/pg_parse_lsn.h"
48 : : #include "miscadmin.h"
49 : : #include "nodes/miscnodes.h"
50 : : #include "pgstat.h"
51 : : #include "postmaster/bgwriter.h"
52 : : #include "postmaster/startup.h"
53 : : #include "replication/slot.h"
54 : : #include "replication/slotsync.h"
55 : : #include "replication/walreceiver.h"
56 : : #include "storage/fd.h"
57 : : #include "storage/ipc.h"
58 : : #include "storage/latch.h"
59 : : #include "storage/pmsignal.h"
60 : : #include "storage/procarray.h"
61 : : #include "storage/spin.h"
62 : : #include "storage/subsystems.h"
63 : : #include "utils/datetime.h"
64 : : #include "utils/fmgrprotos.h"
65 : : #include "utils/guc.h"
66 : : #include "utils/guc_hooks.h"
67 : : #include "utils/pgstat_internal.h"
68 : : #include "utils/pg_lsn.h"
69 : : #include "utils/ps_status.h"
70 : : #include "utils/pg_rusage.h"
71 : : #include "utils/wait_event.h"
72 : :
73 : : /* Unsupported old recovery command file names (relative to $PGDATA) */
74 : : #define RECOVERY_COMMAND_FILE "recovery.conf"
75 : : #define RECOVERY_COMMAND_DONE "recovery.done"
76 : :
77 : : /*
78 : : * GUC support
79 : : */
80 : : const struct config_enum_entry recovery_target_action_options[] = {
81 : : {"pause", RECOVERY_TARGET_ACTION_PAUSE, false},
82 : : {"promote", RECOVERY_TARGET_ACTION_PROMOTE, false},
83 : : {"shutdown", RECOVERY_TARGET_ACTION_SHUTDOWN, false},
84 : : {NULL, 0, false}
85 : : };
86 : :
87 : : /* options formerly taken from recovery.conf for archive recovery */
88 : : char *recoveryRestoreCommand = NULL;
89 : : char *recoveryEndCommand = NULL;
90 : : char *archiveCleanupCommand = NULL;
91 : : RecoveryTargetType recoveryTarget = RECOVERY_TARGET_UNSET;
92 : : bool recoveryTargetInclusive = true;
93 : : int recoveryTargetAction = RECOVERY_TARGET_ACTION_PAUSE;
94 : : TransactionId recoveryTargetXid;
95 : : char *recovery_target_time_string;
96 : : TimestampTz recoveryTargetTime;
97 : : char *recoveryTargetName;
98 : : XLogRecPtr recoveryTargetLSN;
99 : : int recovery_min_apply_delay = 0;
100 : :
101 : : /* options formerly taken from recovery.conf for XLOG streaming */
102 : : char *PrimaryConnInfo = NULL;
103 : : char *PrimarySlotName = NULL;
104 : : bool wal_receiver_create_temp_slot = false;
105 : :
106 : : /*
107 : : * recoveryTargetTimeLineGoal: what the user requested, if any
108 : : *
109 : : * recoveryTargetTLIRequested: numeric value of requested timeline, if constant
110 : : *
111 : : * recoveryTargetTLI: the currently understood target timeline; changes
112 : : *
113 : : * expectedTLEs: a list of TimeLineHistoryEntries for recoveryTargetTLI and
114 : : * the timelines of its known parents, newest first (so recoveryTargetTLI is
115 : : * always the first list member). Only these TLIs are expected to be seen in
116 : : * the WAL segments we read, and indeed only these TLIs will be considered as
117 : : * candidate WAL files to open at all.
118 : : *
119 : : * curFileTLI: the TLI appearing in the name of the current input WAL file.
120 : : * (This is not necessarily the same as the timeline from which we are
121 : : * replaying WAL, which StartupXLOG calls replayTLI, because we could be
122 : : * scanning data that was copied from an ancestor timeline when the current
123 : : * file was created.) During a sequential scan we do not allow this value
124 : : * to decrease.
125 : : */
126 : : RecoveryTargetTimeLineGoal recoveryTargetTimeLineGoal = RECOVERY_TARGET_TIMELINE_LATEST;
127 : : TimeLineID recoveryTargetTLIRequested = 0;
128 : : TimeLineID recoveryTargetTLI = 0;
129 : : static List *expectedTLEs;
130 : : static TimeLineID curFileTLI;
131 : :
132 : : /*
133 : : * When ArchiveRecoveryRequested is set, archive recovery was requested,
134 : : * ie. signal files were present. When InArchiveRecovery is set, we are
135 : : * currently recovering using offline XLOG archives. These variables are only
136 : : * valid in the startup process.
137 : : *
138 : : * When ArchiveRecoveryRequested is true, but InArchiveRecovery is false, we're
139 : : * currently performing crash recovery using only XLOG files in pg_wal, but
140 : : * will switch to using offline XLOG archives as soon as we reach the end of
141 : : * WAL in pg_wal.
142 : : */
143 : : bool ArchiveRecoveryRequested = false;
144 : : bool InArchiveRecovery = false;
145 : :
146 : : /*
147 : : * When StandbyModeRequested is set, standby mode was requested, i.e.
148 : : * standby.signal file was present. When StandbyMode is set, we are currently
149 : : * in standby mode. These variables are only valid in the startup process.
150 : : * They work similarly to ArchiveRecoveryRequested and InArchiveRecovery.
151 : : */
152 : : static bool StandbyModeRequested = false;
153 : : bool StandbyMode = false;
154 : :
155 : : /* was a signal file present at startup? */
156 : : static bool standby_signal_file_found = false;
157 : : static bool recovery_signal_file_found = false;
158 : :
159 : : /*
160 : : * CheckPointLoc is the position of the checkpoint record that determines
161 : : * where to start the replay. It comes from the backup label file or the
162 : : * control file.
163 : : *
164 : : * RedoStartLSN is the checkpoint's REDO location, also from the backup label
165 : : * file or the control file. In standby mode, XLOG streaming usually starts
166 : : * from the position where an invalid record was found. But if we fail to
167 : : * read even the initial checkpoint record, we use the REDO location instead
168 : : * of the checkpoint location as the start position of XLOG streaming.
169 : : * Otherwise we would have to jump backwards to the REDO location after
170 : : * reading the checkpoint record, because the REDO record can precede the
171 : : * checkpoint record.
172 : : */
173 : : static XLogRecPtr CheckPointLoc = InvalidXLogRecPtr;
174 : : static TimeLineID CheckPointTLI = 0;
175 : : static XLogRecPtr RedoStartLSN = InvalidXLogRecPtr;
176 : : static TimeLineID RedoStartTLI = 0;
177 : :
178 : : /*
179 : : * Local copy of SharedHotStandbyActive variable. False actually means "not
180 : : * known, need to check the shared state".
181 : : */
182 : : static bool LocalHotStandbyActive = false;
183 : :
184 : : /*
185 : : * Local copy of SharedPromoteIsTriggered variable. False actually means "not
186 : : * known, need to check the shared state".
187 : : */
188 : : static bool LocalPromoteIsTriggered = false;
189 : :
190 : : /* Has the recovery code requested a walreceiver wakeup? */
191 : : static bool doRequestWalReceiverReply;
192 : :
193 : : /* XLogReader object used to parse the WAL records */
194 : : static XLogReaderState *xlogreader = NULL;
195 : :
196 : : /* XLogPrefetcher object used to consume WAL records with read-ahead */
197 : : static XLogPrefetcher *xlogprefetcher = NULL;
198 : :
199 : : /* Parameters passed down from ReadRecord to the XLogPageRead callback. */
200 : : typedef struct XLogPageReadPrivate
201 : : {
202 : : int emode;
203 : : bool fetching_ckpt; /* are we fetching a checkpoint record? */
204 : : bool randAccess;
205 : : TimeLineID replayTLI;
206 : : } XLogPageReadPrivate;
207 : :
208 : : /* flag to tell XLogPageRead that we have started replaying */
209 : : static bool InRedo = false;
210 : :
211 : : /*
212 : : * Codes indicating where we got a WAL file from during recovery, or where
213 : : * to attempt to get one.
214 : : */
215 : : typedef enum
216 : : {
217 : : XLOG_FROM_ANY = 0, /* request to read WAL from any source */
218 : : XLOG_FROM_ARCHIVE, /* restored using restore_command */
219 : : XLOG_FROM_PG_WAL, /* existing file in pg_wal */
220 : : XLOG_FROM_STREAM, /* streamed from primary */
221 : : } XLogSource;
222 : :
223 : : /* human-readable names for XLogSources, for debugging output */
224 : : static const char *const xlogSourceNames[] = {"any", "archive", "pg_wal", "stream"};
225 : :
226 : : /*
227 : : * readFile is -1 or a kernel FD for the log file segment that's currently
228 : : * open for reading. readSegNo identifies the segment. readOff is the offset
229 : : * of the page just read, readLen indicates how much of it has been read into
230 : : * readBuf, and readSource indicates where we got the currently open file from.
231 : : *
232 : : * Note: we could use Reserve/ReleaseExternalFD to track consumption of this
233 : : * FD too (like for openLogFile in xlog.c); but it doesn't currently seem
234 : : * worthwhile, since the XLOG is not read by general-purpose sessions.
235 : : */
236 : : static int readFile = -1;
237 : : static XLogSegNo readSegNo = 0;
238 : : static uint32 readOff = 0;
239 : : static uint32 readLen = 0;
240 : : static XLogSource readSource = XLOG_FROM_ANY;
241 : :
242 : : /*
243 : : * Keeps track of which source we're currently reading from. This is
244 : : * different from readSource in that this is always set, even when we don't
245 : : * currently have a WAL file open. If lastSourceFailed is set, our last
246 : : * attempt to read from currentSource failed, and we should try another source
247 : : * next.
248 : : *
249 : : * pendingWalRcvRestart is set when a config change occurs that requires a
250 : : * walreceiver restart. This is only valid in XLOG_FROM_STREAM state.
251 : : */
252 : : static XLogSource currentSource = XLOG_FROM_ANY;
253 : : static bool lastSourceFailed = false;
254 : : static bool pendingWalRcvRestart = false;
255 : :
256 : : /*
257 : : * These variables track when we last obtained some WAL data to process,
258 : : * and where we got it from. (XLogReceiptSource is initially the same as
259 : : * readSource, but readSource gets reset to zero when we don't have data
260 : : * to process right now. It is also different from currentSource, which
261 : : * also changes when we try to read from a source and fail, while
262 : : * XLogReceiptSource tracks where we last successfully read some WAL.)
263 : : */
264 : : static TimestampTz XLogReceiptTime = 0;
265 : : static XLogSource XLogReceiptSource = XLOG_FROM_ANY;
266 : :
267 : : /* Local copy of WalRcv->flushedUpto */
268 : : static XLogRecPtr flushedUpto = InvalidXLogRecPtr;
269 : : static TimeLineID receiveTLI = 0;
270 : :
271 : : /*
272 : : * Copy of minRecoveryPoint and backupEndPoint from the control file.
273 : : *
274 : : * In order to reach consistency, we must replay the WAL up to
275 : : * minRecoveryPoint. If backupEndRequired is true, we must also reach
276 : : * backupEndPoint, or if it's invalid, an end-of-backup record corresponding
277 : : * to backupStartPoint.
278 : : *
279 : : * Note: In archive recovery, after consistency has been reached, the
280 : : * functions in xlog.c will start updating minRecoveryPoint in the control
281 : : * file. But this copy of minRecoveryPoint variable reflects the value at the
282 : : * beginning of recovery, and is *not* updated after consistency is reached.
283 : : */
284 : : static XLogRecPtr minRecoveryPoint;
285 : : static TimeLineID minRecoveryPointTLI;
286 : :
287 : : static XLogRecPtr backupStartPoint;
288 : : static XLogRecPtr backupEndPoint;
289 : : static bool backupEndRequired = false;
290 : :
291 : : /*
292 : : * Have we reached a consistent database state? In crash recovery, we have
293 : : * to replay all the WAL, so reachedConsistency is never set. During archive
294 : : * recovery, the database is consistent once minRecoveryPoint is reached.
295 : : *
296 : : * Consistent state means that the system is internally consistent, all
297 : : * the WAL has been replayed up to a certain point, and importantly, there
298 : : * is no trace of later actions on disk.
299 : : *
300 : : * This flag is used only by the startup process and postmaster. When
301 : : * minRecoveryPoint is reached, the startup process sets it to true and
302 : : * sends a PMSIGNAL_RECOVERY_CONSISTENT signal to the postmaster,
303 : : * which then sets it to true upon receiving the signal.
304 : : */
305 : : bool reachedConsistency = false;
306 : :
307 : : /* Buffers dedicated to consistency checks of size BLCKSZ */
308 : : static char *replay_image_masked = NULL;
309 : : static char *primary_image_masked = NULL;
310 : :
311 : : XLogRecoveryCtlData *XLogRecoveryCtl = NULL;
312 : :
313 : : static void XLogRecoveryShmemRequest(void *arg);
314 : : static void XLogRecoveryShmemInit(void *arg);
315 : :
316 : : const ShmemCallbacks XLogRecoveryShmemCallbacks = {
317 : : .request_fn = XLogRecoveryShmemRequest,
318 : : .init_fn = XLogRecoveryShmemInit,
319 : : };
320 : :
321 : : /*
322 : : * abortedRecPtr is the start pointer of a broken record at end of WAL when
323 : : * recovery completes; missingContrecPtr is the location of the first
324 : : * contrecord that went missing. See CreateOverwriteContrecordRecord for
325 : : * details.
326 : : */
327 : : static XLogRecPtr abortedRecPtr;
328 : : static XLogRecPtr missingContrecPtr;
329 : :
330 : : /*
331 : : * if recoveryStopsBefore/After returns true, it saves information of the stop
332 : : * point here
333 : : */
334 : : static TransactionId recoveryStopXid;
335 : : static TimestampTz recoveryStopTime;
336 : : static XLogRecPtr recoveryStopLSN;
337 : : static char recoveryStopName[MAXFNAMELEN];
338 : : static bool recoveryStopAfter;
339 : :
340 : : /* prototypes for local functions */
341 : : static void ApplyWalRecord(XLogReaderState *xlogreader, XLogRecord *record, TimeLineID *replayTLI);
342 : :
343 : : static void EnableStandbyMode(void);
344 : : static void readRecoverySignalFile(void);
345 : : static void validateRecoveryParameters(void);
346 : : static bool read_backup_label(XLogRecPtr *checkPointLoc,
347 : : TimeLineID *backupLabelTLI,
348 : : bool *backupEndRequired, bool *backupFromStandby);
349 : : static bool read_tablespace_map(List **tablespaces);
350 : :
351 : : static void xlogrecovery_redo(XLogReaderState *record, TimeLineID replayTLI);
352 : : static void CheckRecoveryConsistency(void);
353 : : static void rm_redo_error_callback(void *arg);
354 : : #ifdef WAL_DEBUG
355 : : static void xlog_outrec(StringInfo buf, XLogReaderState *record);
356 : : #endif
357 : : static void xlog_block_info(StringInfo buf, XLogReaderState *record);
358 : : static void checkTimeLineSwitch(XLogRecPtr lsn, TimeLineID newTLI,
359 : : TimeLineID prevTLI, TimeLineID replayTLI);
360 : : static bool getRecordTimestamp(XLogReaderState *record, TimestampTz *recordXtime);
361 : : static void verifyBackupPageConsistency(XLogReaderState *record);
362 : :
363 : : static bool recoveryStopsBefore(XLogReaderState *record);
364 : : static bool recoveryStopsAfter(XLogReaderState *record);
365 : : static char *getRecoveryStopReason(void);
366 : : static void recoveryPausesHere(bool endOfRecovery);
367 : : static bool recoveryApplyDelay(XLogReaderState *record);
368 : : static void ConfirmRecoveryPaused(void);
369 : :
370 : : static XLogRecord *ReadRecord(XLogPrefetcher *xlogprefetcher,
371 : : int emode, bool fetching_ckpt,
372 : : TimeLineID replayTLI);
373 : :
374 : : static int XLogPageRead(XLogReaderState *xlogreader, XLogRecPtr targetPagePtr,
375 : : int reqLen, XLogRecPtr targetRecPtr, char *readBuf);
376 : : static XLogPageReadResult WaitForWALToBecomeAvailable(XLogRecPtr RecPtr,
377 : : bool randAccess,
378 : : bool fetching_ckpt,
379 : : XLogRecPtr tliRecPtr,
380 : : TimeLineID replayTLI,
381 : : XLogRecPtr replayLSN,
382 : : bool nonblocking);
383 : : static int emode_for_corrupt_record(int emode, XLogRecPtr RecPtr);
384 : : static XLogRecord *ReadCheckpointRecord(XLogPrefetcher *xlogprefetcher,
385 : : XLogRecPtr RecPtr, TimeLineID replayTLI);
386 : : static bool rescanLatestTimeLine(TimeLineID replayTLI, XLogRecPtr replayLSN);
387 : : static int XLogFileRead(XLogSegNo segno, TimeLineID tli,
388 : : XLogSource source, bool notfoundOk);
389 : : static int XLogFileReadAnyTLI(XLogSegNo segno, XLogSource source);
390 : :
391 : : static bool CheckForStandbyTrigger(void);
392 : : static void SetPromoteIsTriggered(void);
393 : : static bool HotStandbyActiveInReplay(void);
394 : :
395 : : static void SetCurrentChunkStartTime(TimestampTz xtime);
396 : : static void SetLatestXTime(TimestampTz xtime);
397 : : static RecoveryTargetType DetermineRecoveryTargetType(void);
398 : :
399 : : /*
400 : : * Register shared memory for WAL recovery
401 : : */
402 : : static void
213 heikki.linnakangas@i 403 :CBC 1307 : XLogRecoveryShmemRequest(void *arg)
404 : : {
405 : 1307 : ShmemRequestStruct(.name = "XLOG Recovery Ctl",
406 : : .size = sizeof(XLogRecoveryCtlData),
407 : : .ptr = (void **) &XLogRecoveryCtl,
408 : : );
1723 409 : 1307 : }
410 : :
411 : : static void
213 412 : 1303 : XLogRecoveryShmemInit(void *arg)
413 : : {
1723 414 : 1303 : memset(XLogRecoveryCtl, 0, sizeof(XLogRecoveryCtlData));
415 : :
416 : 1303 : SpinLockInit(&XLogRecoveryCtl->info_lck);
417 : 1303 : InitSharedLatch(&XLogRecoveryCtl->recoveryWakeupLatch);
418 : 1303 : ConditionVariableInit(&XLogRecoveryCtl->recoveryNotPausedCV);
419 : 1303 : }
420 : :
421 : : /*
422 : : * A thin wrapper to enable StandbyMode and do other preparatory work as
423 : : * needed.
424 : : */
425 : : static void
1368 rhaas@postgresql.org 426 : 135 : EnableStandbyMode(void)
427 : : {
428 : 135 : StandbyMode = true;
429 : :
430 : : /*
431 : : * To avoid server log bloat, we don't report recovery progress in a
432 : : * standby as it will always be in recovery unless promoted. We disable
433 : : * startup progress timeout in standby mode to avoid calling
434 : : * startup_progress_timeout_handler() unnecessarily.
435 : : */
436 : 135 : disable_startup_progress_timeout();
437 : 135 : }
438 : :
439 : : /*
440 : : * Prepare the system for WAL recovery, if needed.
441 : : *
442 : : * This is called by StartupXLOG() which coordinates the server startup
443 : : * sequence. This function analyzes the control file and the backup label
444 : : * file, if any, and figures out whether we need to perform crash recovery or
445 : : * archive recovery, and how far we need to replay the WAL to reach a
446 : : * consistent state.
447 : : *
448 : : * This doesn't yet change the on-disk state, except for creating the symlinks
449 : : * from table space map file if any, and for fetching WAL files needed to find
450 : : * the checkpoint record. On entry, the caller has already read the control
451 : : * file into memory, and passes it as argument. This function updates it to
452 : : * reflect the recovery state, and the caller is expected to write it back to
453 : : * disk after initializing other subsystems, but before calling
454 : : * PerformWalRecovery().
455 : : *
456 : : * This initializes some global variables like ArchiveRecoveryRequested, and
457 : : * StandbyModeRequested and InRecovery.
458 : : */
459 : : void
1723 heikki.linnakangas@i 460 : 1134 : InitWalRecovery(ControlFileData *ControlFile, bool *wasShutdown_ptr,
461 : : bool *haveBackupLabel_ptr, bool *haveTblspcMap_ptr)
462 : : {
463 : : XLogPageReadPrivate *private;
464 : : struct stat st;
465 : : bool wasShutdown;
466 : : XLogRecord *record;
467 : : DBState dbstate_at_startup;
468 : 1134 : bool haveTblspcMap = false;
469 : 1134 : bool haveBackupLabel = false;
470 : : CheckPoint checkPoint;
471 : 1134 : bool backupFromStandby = false;
472 : :
473 : 1134 : dbstate_at_startup = ControlFile->state;
474 : :
475 : : /*
476 : : * A startup process always starts with an inconsistent database. Set the
477 : : * flag accordingly, even if it was inherited from a postmaster that had
478 : : * already marked the database as consistent. This keeps the invariant
479 : : * local to the startup process without requiring every fork path to clear
480 : : * the flag.
481 : : */
89 fujii@postgresql.org 482 : 1134 : reachedConsistency = false;
483 : :
484 : : /*
485 : : * Initialize on the assumption we want to recover to the latest timeline
486 : : * that's active according to pg_control.
487 : : */
1723 heikki.linnakangas@i 488 : 1134 : if (ControlFile->minRecoveryPointTLI >
489 [ + + ]: 1134 : ControlFile->checkPointCopy.ThisTimeLineID)
490 : 1 : recoveryTargetTLI = ControlFile->minRecoveryPointTLI;
491 : : else
492 : 1133 : recoveryTargetTLI = ControlFile->checkPointCopy.ThisTimeLineID;
493 : :
494 : : /*
495 : : * Check for signal files, and if so set up state for offline recovery
496 : : */
497 : 1134 : readRecoverySignalFile();
498 : 1134 : validateRecoveryParameters();
499 : :
500 : : /*
501 : : * Take ownership of the wakeup latch if we're going to sleep during
502 : : * recovery, if required.
503 : : */
504 [ + + ]: 1131 : if (ArchiveRecoveryRequested)
505 : 140 : OwnLatch(&XLogRecoveryCtl->recoveryWakeupLatch);
506 : :
507 : : /*
508 : : * Set the WAL reading processor now, as it will be needed when reading
509 : : * the checkpoint record required (backup_label or not).
510 : : */
330 michael@paquier.xyz 511 : 1131 : private = palloc0_object(XLogPageReadPrivate);
1723 heikki.linnakangas@i 512 : 1131 : xlogreader =
513 : 1131 : XLogReaderAllocate(wal_segment_size, NULL,
514 : 1131 : XL_ROUTINE(.page_read = &XLogPageRead,
515 : : .segment_open = NULL,
516 : : .segment_close = wal_segment_close),
517 : : private);
518 [ - + ]: 1131 : if (!xlogreader)
1723 heikki.linnakangas@i 519 [ # # ]:UBC 0 : ereport(ERROR,
520 : : (errcode(ERRCODE_OUT_OF_MEMORY),
521 : : errmsg("out of memory"),
522 : : errdetail("Failed while allocating a WAL reading processor.")));
1723 heikki.linnakangas@i 523 :CBC 1131 : xlogreader->system_identifier = ControlFile->system_identifier;
524 : :
525 : : /*
526 : : * Set the WAL decode buffer size. This limits how far ahead we can read
527 : : * in the WAL.
528 : : */
1673 tmunro@postgresql.or 529 : 1131 : XLogReaderSetDecodeBuffer(xlogreader, NULL, wal_decode_buffer_size);
530 : :
531 : : /* Create a WAL prefetcher. */
532 : 1131 : xlogprefetcher = XLogPrefetcherAllocate(xlogreader);
533 : :
534 : : /*
535 : : * Allocate two page buffers dedicated to WAL consistency checks. We do
536 : : * it this way, rather than just making static arrays, for two reasons:
537 : : * (1) no need to waste the storage in most instantiations of the backend;
538 : : * (2) a static char array isn't guaranteed to have any particular
539 : : * alignment, whereas palloc() will provide MAXALIGN'd storage.
540 : : */
1723 heikki.linnakangas@i 541 : 1131 : replay_image_masked = (char *) palloc(BLCKSZ);
542 : 1131 : primary_image_masked = (char *) palloc(BLCKSZ);
543 : :
544 : : /*
545 : : * Read the backup_label file. We want to run this part of the recovery
546 : : * process after checking for signal files and after performing validation
547 : : * of the recovery parameters.
548 : : */
549 [ + + ]: 1131 : if (read_backup_label(&CheckPointLoc, &CheckPointTLI, &backupEndRequired,
550 : : &backupFromStandby))
551 : : {
552 : 104 : List *tablespaces = NIL;
553 : :
554 : : /*
555 : : * Archive recovery was requested, and thanks to the backup label
556 : : * file, we know how far we need to replay to reach consistency. Enter
557 : : * archive recovery directly.
558 : : */
559 : 104 : InArchiveRecovery = true;
560 [ + + ]: 104 : if (StandbyModeRequested)
1368 rhaas@postgresql.org 561 : 87 : EnableStandbyMode();
562 : :
563 : : /*
564 : : * Omitting backup_label when creating a new replica, PITR node etc.
565 : : * unfortunately is a common cause of corruption. Logging that
566 : : * backup_label was used makes it a bit easier to exclude that as the
567 : : * cause of observed corruption.
568 : : *
569 : : * Do so before we try to read the checkpoint record (which can fail),
570 : : * as otherwise it can be hard to understand why a checkpoint other
571 : : * than ControlFile->checkPoint is used.
572 : : */
1015 michael@paquier.xyz 573 [ + - ]: 104 : ereport(LOG,
574 : : errmsg("starting backup recovery with redo LSN %X/%08X, checkpoint LSN %X/%08X, on timeline ID %u",
575 : : LSN_FORMAT_ARGS(RedoStartLSN),
576 : : LSN_FORMAT_ARGS(CheckPointLoc),
577 : : CheckPointTLI));
578 : :
579 : : /*
580 : : * When a backup_label file is present, we want to roll forward from
581 : : * the checkpoint it identifies, rather than using pg_control.
582 : : */
1569 fujii@postgresql.org 583 : 104 : record = ReadCheckpointRecord(xlogprefetcher, CheckPointLoc,
584 : : CheckPointTLI);
1723 heikki.linnakangas@i 585 [ + + ]: 104 : if (record != NULL)
586 : : {
587 : 103 : memcpy(&checkPoint, XLogRecGetData(xlogreader), sizeof(CheckPoint));
588 : 103 : wasShutdown = ((record->xl_info & ~XLR_INFO_MASK) == XLOG_CHECKPOINT_SHUTDOWN);
589 [ + + ]: 103 : ereport(DEBUG1,
590 : : errmsg_internal("checkpoint record is at %X/%08X",
591 : : LSN_FORMAT_ARGS(CheckPointLoc)));
592 : 103 : InRecovery = true; /* force recovery even if SHUTDOWNED */
593 : :
594 : : /*
595 : : * Make sure that REDO location exists. This may not be the case
596 : : * if there was a crash during an online backup, which left a
597 : : * backup_label around that references a WAL segment that's
598 : : * already been archived.
599 : : */
600 [ + + ]: 103 : if (checkPoint.redo < CheckPointLoc)
601 : : {
1673 tmunro@postgresql.or 602 : 101 : XLogPrefetcherBeginRead(xlogprefetcher, checkPoint.redo);
603 [ + + ]: 101 : if (!ReadRecord(xlogprefetcher, LOG, false,
604 : : checkPoint.ThisTimeLineID))
1723 heikki.linnakangas@i 605 [ + - ]:GBC 1 : ereport(FATAL,
606 : : errmsg("could not find redo location %X/%08X referenced by checkpoint record at %X/%08X",
607 : : LSN_FORMAT_ARGS(checkPoint.redo), LSN_FORMAT_ARGS(CheckPointLoc)),
608 : : errhint("If you are restoring from a backup, touch \"%s/recovery.signal\" or \"%s/standby.signal\" and add required recovery options.\n"
609 : : "If you are not restoring from a backup, try removing the file \"%s/backup_label\".\n"
610 : : "Be careful: removing \"%s/backup_label\" will result in a corrupt cluster if restoring from a backup.",
611 : : DataDir, DataDir, DataDir, DataDir));
612 : : }
613 : : }
614 : : else
615 : : {
616 [ + - ]: 1 : ereport(FATAL,
617 : : errmsg("could not locate required checkpoint record at %X/%08X",
618 : : LSN_FORMAT_ARGS(CheckPointLoc)),
619 : : errhint("If you are restoring from a backup, touch \"%s/recovery.signal\" or \"%s/standby.signal\" and add required recovery options.\n"
620 : : "If you are not restoring from a backup, try removing the file \"%s/backup_label\".\n"
621 : : "Be careful: removing \"%s/backup_label\" will result in a corrupt cluster if restoring from a backup.",
622 : : DataDir, DataDir, DataDir, DataDir));
623 : : wasShutdown = false; /* keep compiler quiet */
624 : : }
625 : :
626 : : /* Read the tablespace_map file if present and create symlinks. */
1723 heikki.linnakangas@i 627 [ + + ]:CBC 102 : if (read_tablespace_map(&tablespaces))
628 : : {
629 : : ListCell *lc;
630 : :
631 [ + - + + : 4 : foreach(lc, tablespaces)
+ + ]
632 : : {
633 : 2 : tablespaceinfo *ti = lfirst(lc);
634 : : char *linkloc;
635 : :
793 michael@paquier.xyz 636 : 2 : linkloc = psprintf("%s/%u", PG_TBLSPC_DIR, ti->oid);
637 : :
638 : : /*
639 : : * Remove the existing symlink if any and Create the symlink
640 : : * under PGDATA.
641 : : */
1723 heikki.linnakangas@i 642 : 2 : remove_tablespace_symlink(linkloc);
643 : :
644 [ - + ]: 2 : if (symlink(ti->path, linkloc) < 0)
1723 heikki.linnakangas@i 645 [ # # ]:UBC 0 : ereport(ERROR,
646 : : (errcode_for_file_access(),
647 : : errmsg("could not create symbolic link \"%s\": %m",
648 : : linkloc)));
649 : :
1723 heikki.linnakangas@i 650 :CBC 2 : pfree(ti->path);
651 : 2 : pfree(ti);
652 : : }
653 : :
654 : : /* tell the caller to delete it later */
655 : 2 : haveTblspcMap = true;
656 : : }
657 : :
658 : : /* tell the caller to delete it later */
659 : 102 : haveBackupLabel = true;
660 : : }
661 : : else
662 : : {
663 : : /* No backup_label file has been found if we are here. */
664 : :
665 : : /*
666 : : * If tablespace_map file is present without backup_label file, there
667 : : * is no use of such file. There is no harm in retaining it, but it
668 : : * is better to get rid of the map file so that we don't have any
669 : : * redundant file in data directory and it will avoid any sort of
670 : : * confusion. It seems prudent though to just rename the file out of
671 : : * the way rather than delete it completely, also we ignore any error
672 : : * that occurs in rename operation as even if map file is present
673 : : * without backup_label file, it is harmless.
674 : : */
675 [ + + ]: 1027 : if (stat(TABLESPACE_MAP, &st) == 0)
676 : : {
677 : 1 : unlink(TABLESPACE_MAP_OLD);
678 [ + - ]: 1 : if (durable_rename(TABLESPACE_MAP, TABLESPACE_MAP_OLD, DEBUG1) == 0)
679 [ + - ]: 1 : ereport(LOG,
680 : : (errmsg("ignoring file \"%s\" because no file \"%s\" exists",
681 : : TABLESPACE_MAP, BACKUP_LABEL_FILE),
682 : : errdetail("File \"%s\" was renamed to \"%s\".",
683 : : TABLESPACE_MAP, TABLESPACE_MAP_OLD)));
684 : : else
1723 heikki.linnakangas@i 685 [ # # ]:UBC 0 : ereport(LOG,
686 : : (errmsg("ignoring file \"%s\" because no file \"%s\" exists",
687 : : TABLESPACE_MAP, BACKUP_LABEL_FILE),
688 : : errdetail("Could not rename file \"%s\" to \"%s\": %m.",
689 : : TABLESPACE_MAP, TABLESPACE_MAP_OLD)));
690 : : }
691 : :
692 : : /*
693 : : * It's possible that archive recovery was requested, but we don't
694 : : * know how far we need to replay the WAL before we reach consistency.
695 : : * This can happen for example if a base backup is taken from a
696 : : * running server using an atomic filesystem snapshot, without calling
697 : : * pg_backup_start/stop. Or if you just kill a running primary server
698 : : * and put it into archive recovery by creating a recovery signal
699 : : * file.
700 : : *
701 : : * Our strategy in that case is to perform crash recovery first,
702 : : * replaying all the WAL present in pg_wal, and only enter archive
703 : : * recovery after that.
704 : : *
705 : : * But usually we already know how far we need to replay the WAL (up
706 : : * to minRecoveryPoint, up to backupEndPoint, or until we see an
707 : : * end-of-backup record), and we can enter archive recovery directly.
708 : : */
1723 heikki.linnakangas@i 709 [ + + ]:CBC 1027 : if (ArchiveRecoveryRequested &&
364 alvherre@kurilemu.de 710 [ + + ]: 48 : (XLogRecPtrIsValid(ControlFile->minRecoveryPoint) ||
1723 heikki.linnakangas@i 711 [ + - ]: 10 : ControlFile->backupEndRequired ||
364 alvherre@kurilemu.de 712 [ + - ]: 10 : XLogRecPtrIsValid(ControlFile->backupEndPoint) ||
1723 heikki.linnakangas@i 713 [ + + ]: 10 : ControlFile->state == DB_SHUTDOWNED))
714 : : {
715 : 47 : InArchiveRecovery = true;
716 [ + - ]: 47 : if (StandbyModeRequested)
1368 rhaas@postgresql.org 717 : 47 : EnableStandbyMode();
718 : : }
719 : :
720 : : /*
721 : : * For the same reason as when starting up with backup_label present,
722 : : * emit a log message when we continue initializing from a base
723 : : * backup.
724 : : */
364 alvherre@kurilemu.de 725 [ - + ]: 1027 : if (XLogRecPtrIsValid(ControlFile->backupStartPoint))
1015 michael@paquier.xyz 726 [ # # ]:UBC 0 : ereport(LOG,
727 : : errmsg("restarting backup recovery with redo LSN %X/%08X",
728 : : LSN_FORMAT_ARGS(ControlFile->backupStartPoint)));
729 : :
730 : : /* Get the last valid checkpoint record. */
1723 heikki.linnakangas@i 731 :CBC 1027 : CheckPointLoc = ControlFile->checkPoint;
732 : 1027 : CheckPointTLI = ControlFile->checkPointCopy.ThisTimeLineID;
733 : 1027 : RedoStartLSN = ControlFile->checkPointCopy.redo;
734 : 1027 : RedoStartTLI = ControlFile->checkPointCopy.ThisTimeLineID;
1569 fujii@postgresql.org 735 : 1027 : record = ReadCheckpointRecord(xlogprefetcher, CheckPointLoc,
736 : : CheckPointTLI);
1723 heikki.linnakangas@i 737 [ + + ]: 1027 : if (record != NULL)
738 : : {
739 [ + + ]: 1026 : ereport(DEBUG1,
740 : : errmsg_internal("checkpoint record is at %X/%08X",
741 : : LSN_FORMAT_ARGS(CheckPointLoc)));
742 : : }
743 : : else
744 : : {
745 : : /*
746 : : * We used to attempt to go back to a secondary checkpoint record
747 : : * here, but only when not in standby mode. We now just fail if we
748 : : * can't read the last checkpoint because this allows us to
749 : : * simplify processing around checkpoints.
750 : : */
240 michael@paquier.xyz 751 [ + - ]: 1 : ereport(FATAL,
752 : : errmsg("could not locate a valid checkpoint record at %X/%08X",
753 : : LSN_FORMAT_ARGS(CheckPointLoc)));
754 : : }
1723 heikki.linnakangas@i 755 : 1026 : memcpy(&checkPoint, XLogRecGetData(xlogreader), sizeof(CheckPoint));
756 : 1026 : wasShutdown = ((record->xl_info & ~XLR_INFO_MASK) == XLOG_CHECKPOINT_SHUTDOWN);
757 : :
758 : : /* Make sure that REDO location exists. */
324 michael@paquier.xyz 759 [ + + ]: 1026 : if (checkPoint.redo < CheckPointLoc)
760 : : {
761 : 47 : XLogPrefetcherBeginRead(xlogprefetcher, checkPoint.redo);
762 [ + + ]: 47 : if (!ReadRecord(xlogprefetcher, LOG, false, checkPoint.ThisTimeLineID))
763 [ + - ]: 1 : ereport(FATAL,
764 : : errmsg("could not find redo location %X/%08X referenced by checkpoint record at %X/%08X",
765 : : LSN_FORMAT_ARGS(checkPoint.redo), LSN_FORMAT_ARGS(CheckPointLoc)));
766 : : }
767 : : }
768 : :
1102 769 [ + + ]: 1127 : if (ArchiveRecoveryRequested)
770 : : {
771 [ + + ]: 140 : if (StandbyModeRequested)
772 [ + - ]: 135 : ereport(LOG,
773 : : (errmsg("entering standby mode")));
774 [ - + ]: 5 : else if (recoveryTarget == RECOVERY_TARGET_XID)
1102 michael@paquier.xyz 775 [ # # ]:UBC 0 : ereport(LOG,
776 : : (errmsg("starting point-in-time recovery to XID %u",
777 : : recoveryTargetXid)));
1102 michael@paquier.xyz 778 [ - + ]:CBC 5 : else if (recoveryTarget == RECOVERY_TARGET_TIME)
1102 michael@paquier.xyz 779 [ # # ]:UBC 0 : ereport(LOG,
780 : : (errmsg("starting point-in-time recovery to %s",
781 : : timestamptz_to_str(recoveryTargetTime))));
1102 michael@paquier.xyz 782 [ + + ]:CBC 5 : else if (recoveryTarget == RECOVERY_TARGET_NAME)
783 [ + - ]: 3 : ereport(LOG,
784 : : (errmsg("starting point-in-time recovery to \"%s\"",
785 : : recoveryTargetName)));
786 [ - + ]: 2 : else if (recoveryTarget == RECOVERY_TARGET_LSN)
1102 michael@paquier.xyz 787 [ # # ]:UBC 0 : ereport(LOG,
788 : : errmsg("starting point-in-time recovery to WAL location (LSN) \"%X/%08X\"",
789 : : LSN_FORMAT_ARGS(recoveryTargetLSN)));
1102 michael@paquier.xyz 790 [ - + ]:CBC 2 : else if (recoveryTarget == RECOVERY_TARGET_IMMEDIATE)
1102 michael@paquier.xyz 791 [ # # ]:UBC 0 : ereport(LOG,
792 : : (errmsg("starting point-in-time recovery to earliest consistent point")));
793 : : else
1102 michael@paquier.xyz 794 [ + - ]:CBC 2 : ereport(LOG,
795 : : (errmsg("starting archive recovery")));
796 : : }
797 : :
798 : : /*
799 : : * If the location of the checkpoint record is not on the expected
800 : : * timeline in the history of the requested timeline, we cannot proceed:
801 : : * the backup is not part of the history of the requested timeline.
802 : : */
1723 heikki.linnakangas@i 803 [ - + ]: 1127 : Assert(expectedTLEs); /* was initialized by reading checkpoint
804 : : * record */
805 [ - + ]: 1127 : if (tliOfPointInHistory(CheckPointLoc, expectedTLEs) !=
806 : : CheckPointTLI)
807 : : {
808 : : XLogRecPtr switchpoint;
809 : :
810 : : /*
811 : : * tliSwitchPoint will throw an error if the checkpoint's timeline is
812 : : * not in expectedTLEs at all.
813 : : */
623 michael@paquier.xyz 814 :UBC 0 : switchpoint = tliSwitchPoint(CheckPointTLI, expectedTLEs, NULL);
1723 heikki.linnakangas@i 815 [ # # # # ]: 0 : ereport(FATAL,
816 : : (errmsg("requested timeline %u is not a child of this server's history",
817 : : recoveryTargetTLI),
818 : : /* translator: %s is a backup_label file or a pg_control file */
819 : : errdetail("Latest checkpoint in file \"%s\" is at %X/%08X on timeline %u, but in the history of the requested timeline, the server forked off from that timeline at %X/%08X.",
820 : : haveBackupLabel ? "backup_label" : "pg_control",
821 : : LSN_FORMAT_ARGS(CheckPointLoc),
822 : : CheckPointTLI,
823 : : LSN_FORMAT_ARGS(switchpoint))));
824 : : }
825 : :
826 : : /*
827 : : * The min recovery point should be part of the requested timeline's
828 : : * history, too.
829 : : */
364 alvherre@kurilemu.de 830 [ + + ]:CBC 1127 : if (XLogRecPtrIsValid(ControlFile->minRecoveryPoint) &&
1723 heikki.linnakangas@i 831 : 49 : tliOfPointInHistory(ControlFile->minRecoveryPoint - 1, expectedTLEs) !=
832 [ - + ]: 49 : ControlFile->minRecoveryPointTLI)
1723 heikki.linnakangas@i 833 [ # # ]:UBC 0 : ereport(FATAL,
834 : : errmsg("requested timeline %u does not contain minimum recovery point %X/%08X on timeline %u",
835 : : recoveryTargetTLI,
836 : : LSN_FORMAT_ARGS(ControlFile->minRecoveryPoint),
837 : : ControlFile->minRecoveryPointTLI));
838 : :
1723 heikki.linnakangas@i 839 [ + + + + ]:CBC 1127 : ereport(DEBUG1,
840 : : errmsg_internal("redo record is at %X/%08X; shutdown %s",
841 : : LSN_FORMAT_ARGS(checkPoint.redo),
842 : : wasShutdown ? "true" : "false"));
843 [ + + ]: 1127 : ereport(DEBUG1,
844 : : (errmsg_internal("next transaction ID: " UINT64_FORMAT "; next OID: %" PRIu64,
845 : : U64FromFullTransactionId(checkPoint.nextXid),
846 : : checkPoint.nextOid)));
847 [ + + ]: 1127 : ereport(DEBUG1,
848 : : (errmsg_internal("next MultiXactId: %u; next MultiXactOffset: %" PRIu64,
849 : : checkPoint.nextMulti, checkPoint.nextMultiOffset)));
850 [ + + ]: 1127 : ereport(DEBUG1,
851 : : (errmsg_internal("oldest unfrozen transaction ID: %u, in database %u",
852 : : checkPoint.oldestXid, checkPoint.oldestXidDB)));
853 [ + + ]: 1127 : ereport(DEBUG1,
854 : : (errmsg_internal("oldest MultiXactId: %u, in database %u",
855 : : checkPoint.oldestMulti, checkPoint.oldestMultiDB)));
856 [ + + ]: 1127 : ereport(DEBUG1,
857 : : (errmsg_internal("commit timestamp Xid oldest/newest: %u/%u",
858 : : checkPoint.oldestCommitTsXid,
859 : : checkPoint.newestCommitTsXid)));
860 [ - + ]: 1127 : if (!TransactionIdIsNormal(XidFromFullTransactionId(checkPoint.nextXid)))
1723 heikki.linnakangas@i 861 [ # # ]:UBC 0 : ereport(PANIC,
862 : : (errmsg("invalid next transaction ID")));
863 : :
864 : : /* sanity check */
1723 heikki.linnakangas@i 865 [ - + ]:CBC 1127 : if (checkPoint.redo > CheckPointLoc)
1723 heikki.linnakangas@i 866 [ # # ]:UBC 0 : ereport(PANIC,
867 : : (errmsg("invalid redo in checkpoint record")));
868 : :
869 : : /*
870 : : * Check whether we need to force recovery from WAL. If it appears to
871 : : * have been a clean shutdown and we did not have a recovery signal file,
872 : : * then assume no recovery needed.
873 : : */
1723 heikki.linnakangas@i 874 [ + + ]:CBC 1127 : if (checkPoint.redo < CheckPointLoc)
875 : : {
876 [ - + ]: 146 : if (wasShutdown)
1723 heikki.linnakangas@i 877 [ # # ]:UBC 0 : ereport(PANIC,
878 : : (errmsg("invalid redo record in shutdown checkpoint")));
1723 heikki.linnakangas@i 879 :CBC 146 : InRecovery = true;
880 : : }
881 [ + + ]: 981 : else if (ControlFile->state != DB_SHUTDOWNED)
882 : 100 : InRecovery = true;
883 [ + + ]: 881 : else if (ArchiveRecoveryRequested)
884 : : {
885 : : /* force recovery due to presence of recovery signal file */
886 : 9 : InRecovery = true;
887 : : }
888 : :
889 : : /*
890 : : * If recovery is needed, update our in-memory copy of pg_control to show
891 : : * that we are recovering and to show the selected checkpoint as the place
892 : : * we are starting from. We also mark pg_control with any minimum recovery
893 : : * stop point obtained from a backup history file.
894 : : *
895 : : * We don't write the changes to disk yet, though. Only do that after
896 : : * initializing various subsystems.
897 : : */
898 [ + + ]: 1127 : if (InRecovery)
899 : : {
900 [ + + ]: 255 : if (InArchiveRecovery)
901 : : {
902 : 149 : ControlFile->state = DB_IN_ARCHIVE_RECOVERY;
903 : : }
904 : : else
905 : : {
906 [ + - ]: 106 : ereport(LOG,
907 : : (errmsg("database system was not properly shut down; "
908 : : "automatic recovery in progress")));
909 [ + + ]: 106 : if (recoveryTargetTLI > ControlFile->checkPointCopy.ThisTimeLineID)
910 [ + - ]: 1 : ereport(LOG,
911 : : (errmsg("crash recovery starts in timeline %u "
912 : : "and has target timeline %u",
913 : : ControlFile->checkPointCopy.ThisTimeLineID,
914 : : recoveryTargetTLI)));
915 : 106 : ControlFile->state = DB_IN_CRASH_RECOVERY;
916 : : }
917 : 255 : ControlFile->checkPoint = CheckPointLoc;
918 : 255 : ControlFile->checkPointCopy = checkPoint;
919 [ + + ]: 255 : if (InArchiveRecovery)
920 : : {
921 : : /* initialize minRecoveryPoint if not set yet */
922 [ + + ]: 149 : if (ControlFile->minRecoveryPoint < checkPoint.redo)
923 : : {
924 : 101 : ControlFile->minRecoveryPoint = checkPoint.redo;
925 : 101 : ControlFile->minRecoveryPointTLI = checkPoint.ThisTimeLineID;
926 : : }
927 : : }
928 : :
929 : : /*
930 : : * Set backupStartPoint if we're starting recovery from a base backup.
931 : : *
932 : : * Also set backupEndPoint and use minRecoveryPoint as the backup end
933 : : * location if we're starting recovery from a base backup which was
934 : : * taken from a standby. In this case, the database system status in
935 : : * pg_control must indicate that the database was already in recovery.
936 : : * Usually that will be DB_IN_ARCHIVE_RECOVERY but also can be
937 : : * DB_SHUTDOWNED_IN_RECOVERY if recovery previously was interrupted
938 : : * before reaching this point; e.g. because restore_command or
939 : : * primary_conninfo were faulty.
940 : : *
941 : : * Any other state indicates that the backup somehow became corrupted
942 : : * and we can't sensibly continue with recovery.
943 : : */
944 [ + + ]: 255 : if (haveBackupLabel)
945 : : {
946 : 102 : ControlFile->backupStartPoint = checkPoint.redo;
947 : 102 : ControlFile->backupEndRequired = backupEndRequired;
948 : :
949 [ + + ]: 102 : if (backupFromStandby)
950 : : {
951 [ - + - - ]: 10 : if (dbstate_at_startup != DB_IN_ARCHIVE_RECOVERY &&
952 : : dbstate_at_startup != DB_SHUTDOWNED_IN_RECOVERY)
1723 heikki.linnakangas@i 953 [ # # ]:UBC 0 : ereport(FATAL,
954 : : (errmsg("backup_label contains data inconsistent with control file"),
955 : : errhint("This means that the backup is corrupted and you will "
956 : : "have to use another backup for recovery.")));
1723 heikki.linnakangas@i 957 :CBC 10 : ControlFile->backupEndPoint = ControlFile->minRecoveryPoint;
958 : : }
959 : : }
960 : : }
961 : :
962 : : /* remember these, so that we know when we have reached consistency */
963 : 1127 : backupStartPoint = ControlFile->backupStartPoint;
964 : 1127 : backupEndRequired = ControlFile->backupEndRequired;
965 : 1127 : backupEndPoint = ControlFile->backupEndPoint;
966 [ + + ]: 1127 : if (InArchiveRecovery)
967 : : {
968 : 149 : minRecoveryPoint = ControlFile->minRecoveryPoint;
969 : 149 : minRecoveryPointTLI = ControlFile->minRecoveryPointTLI;
970 : : }
971 : : else
972 : : {
973 : 978 : minRecoveryPoint = InvalidXLogRecPtr;
974 : 978 : minRecoveryPointTLI = 0;
975 : : }
976 : :
977 : : /*
978 : : * Start recovery assuming that the final record isn't lost.
979 : : */
980 : 1127 : abortedRecPtr = InvalidXLogRecPtr;
981 : 1127 : missingContrecPtr = InvalidXLogRecPtr;
982 : :
983 : 1127 : *wasShutdown_ptr = wasShutdown;
984 : 1127 : *haveBackupLabel_ptr = haveBackupLabel;
985 : 1127 : *haveTblspcMap_ptr = haveTblspcMap;
986 : 1127 : }
987 : :
988 : : /*
989 : : * See if there are any recovery signal files and if so, set state for
990 : : * recovery.
991 : : *
992 : : * See if there is a recovery command file (recovery.conf), and if so
993 : : * throw an ERROR since as of PG12 we no longer recognize that.
994 : : */
995 : : static void
996 : 1134 : readRecoverySignalFile(void)
997 : : {
998 : : struct stat stat_buf;
999 : :
1000 [ + + ]: 1134 : if (IsBootstrapProcessingMode())
1001 : 993 : return;
1002 : :
1003 : : /*
1004 : : * Check for old recovery API file: recovery.conf
1005 : : */
1006 [ - + ]: 1075 : if (stat(RECOVERY_COMMAND_FILE, &stat_buf) == 0)
1723 heikki.linnakangas@i 1007 [ # # ]:UBC 0 : ereport(FATAL,
1008 : : (errcode_for_file_access(),
1009 : : errmsg("using recovery command file \"%s\" is not supported",
1010 : : RECOVERY_COMMAND_FILE)));
1011 : :
1012 : : /*
1013 : : * Remove unused .done file, if present. Ignore if absent.
1014 : : */
1723 heikki.linnakangas@i 1015 :CBC 1075 : unlink(RECOVERY_COMMAND_DONE);
1016 : :
1017 : : /*
1018 : : * Check for recovery signal files and if found, fsync them since they
1019 : : * represent server state information. We don't sweat too much about the
1020 : : * possibility of fsync failure, however.
1021 : : */
1022 [ + + ]: 1075 : if (stat(STANDBY_SIGNAL_FILE, &stat_buf) == 0)
1023 : : {
1024 : : int fd;
1025 : :
1026 : 136 : fd = BasicOpenFilePerm(STANDBY_SIGNAL_FILE, O_RDWR | PG_BINARY,
1027 : : S_IRUSR | S_IWUSR);
1028 [ + - ]: 136 : if (fd >= 0)
1029 : : {
1030 : 136 : (void) pg_fsync(fd);
1031 : 136 : close(fd);
1032 : : }
1033 : 136 : standby_signal_file_found = true;
1034 : : }
1035 : :
262 fujii@postgresql.org 1036 [ + + ]: 1075 : if (stat(RECOVERY_SIGNAL_FILE, &stat_buf) == 0)
1037 : : {
1038 : : int fd;
1039 : :
1723 heikki.linnakangas@i 1040 : 7 : fd = BasicOpenFilePerm(RECOVERY_SIGNAL_FILE, O_RDWR | PG_BINARY,
1041 : : S_IRUSR | S_IWUSR);
1042 [ + - ]: 7 : if (fd >= 0)
1043 : : {
1044 : 7 : (void) pg_fsync(fd);
1045 : 7 : close(fd);
1046 : : }
1047 : 7 : recovery_signal_file_found = true;
1048 : : }
1049 : :
1050 : : /*
1051 : : * If both signal files are present, standby signal file takes precedence.
1052 : : * If neither is present then we won't enter archive recovery.
1053 : : */
1054 : 1075 : StandbyModeRequested = false;
1055 : 1075 : ArchiveRecoveryRequested = false;
1056 [ + + ]: 1075 : if (standby_signal_file_found)
1057 : : {
1058 : 136 : StandbyModeRequested = true;
1059 : 136 : ArchiveRecoveryRequested = true;
1060 : : }
1061 [ + + ]: 939 : else if (recovery_signal_file_found)
1062 : : {
1063 : 5 : StandbyModeRequested = false;
1064 : 5 : ArchiveRecoveryRequested = true;
1065 : : }
1066 : : else
1067 : 934 : return;
1068 : :
1069 : : /*
1070 : : * We don't support standby mode in standalone backends; that requires
1071 : : * other processes such as the WAL receiver to be alive.
1072 : : */
1073 [ + + - + ]: 141 : if (StandbyModeRequested && !IsUnderPostmaster)
1723 heikki.linnakangas@i 1074 [ # # ]:UBC 0 : ereport(FATAL,
1075 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1076 : : errmsg("standby mode is not supported by single-user servers")));
1077 : : }
1078 : :
1079 : : static void
1723 heikki.linnakangas@i 1080 :CBC 1134 : validateRecoveryParameters(void)
1081 : : {
1082 : : /* Reject conflicting targets even when recovery was not requested */
108 fujii@postgresql.org 1083 :GNC 1134 : recoveryTarget = DetermineRecoveryTargetType();
1084 : :
1723 heikki.linnakangas@i 1085 [ + + ]:CBC 1132 : if (!ArchiveRecoveryRequested)
1086 : 991 : return;
1087 : :
1088 : : /*
1089 : : * Check for compulsory parameters
1090 : : */
1091 [ + + ]: 141 : if (StandbyModeRequested)
1092 : : {
1093 [ + - + + ]: 136 : if ((PrimaryConnInfo == NULL || strcmp(PrimaryConnInfo, "") == 0) &&
1094 [ + - + + ]: 14 : (recoveryRestoreCommand == NULL || strcmp(recoveryRestoreCommand, "") == 0))
1095 [ + - ]: 3 : ereport(WARNING,
1096 : : (errmsg("specified neither \"primary_conninfo\" nor \"restore_command\""),
1097 : : errhint("The database server will regularly poll the pg_wal subdirectory to check for files placed there.")));
1098 : : }
1099 : : else
1100 : : {
1101 [ + - ]: 5 : if (recoveryRestoreCommand == NULL ||
1102 [ - + ]: 5 : strcmp(recoveryRestoreCommand, "") == 0)
1723 heikki.linnakangas@i 1103 [ # # ]:UBC 0 : ereport(FATAL,
1104 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1105 : : errmsg("must specify \"restore_command\" when standby mode is not enabled")));
1106 : : }
1107 : :
1108 : : /*
1109 : : * Override any inconsistent requests. Note that this is a change of
1110 : : * behaviour in 9.5; prior to this we simply ignored a request to pause if
1111 : : * hot_standby = off, which was surprising behaviour.
1112 : : */
1723 heikki.linnakangas@i 1113 [ + + ]:CBC 141 : if (recoveryTargetAction == RECOVERY_TARGET_ACTION_PAUSE &&
1114 [ + + ]: 134 : !EnableHotStandby)
1115 : 3 : recoveryTargetAction = RECOVERY_TARGET_ACTION_SHUTDOWN;
1116 : :
1117 : : /*
1118 : : * Final parsing of recovery_target_time string; see also
1119 : : * check_recovery_target_time().
1120 : : */
1121 [ - + ]: 141 : if (recoveryTarget == RECOVERY_TARGET_TIME)
1122 : : {
1723 heikki.linnakangas@i 1123 :UBC 0 : recoveryTargetTime = DatumGetTimestampTz(DirectFunctionCall3(timestamptz_in,
1124 : : CStringGetDatum(recovery_target_time_string),
1125 : : ObjectIdGetDatum(InvalidOid),
1126 : : Int32GetDatum(-1)));
1127 : : }
1128 : :
1129 : : /*
1130 : : * If user specified recovery_target_timeline, validate it or compute the
1131 : : * "latest" value. We can't do this until after we've gotten the restore
1132 : : * command and set InArchiveRecovery, because we need to fetch timeline
1133 : : * history files from the archive.
1134 : : */
1723 heikki.linnakangas@i 1135 [ - + ]:CBC 141 : if (recoveryTargetTimeLineGoal == RECOVERY_TARGET_TIMELINE_NUMERIC)
1136 : : {
1723 heikki.linnakangas@i 1137 :UBC 0 : TimeLineID rtli = recoveryTargetTLIRequested;
1138 : :
1139 : : /* Timeline 1 does not have a history file, all else should */
1140 [ # # # # ]: 0 : if (rtli != 1 && !existsTimeLineHistory(rtli))
1141 [ # # ]: 0 : ereport(FATAL,
1142 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1143 : : errmsg("recovery target timeline %u does not exist",
1144 : : rtli)));
1145 : 0 : recoveryTargetTLI = rtli;
1146 : : }
1723 heikki.linnakangas@i 1147 [ + - ]:CBC 141 : else if (recoveryTargetTimeLineGoal == RECOVERY_TARGET_TIMELINE_LATEST)
1148 : : {
1149 : : /* We start the "latest" search from pg_control's timeline */
1150 : 141 : recoveryTargetTLI = findNewestTimeLine(recoveryTargetTLI);
1151 : : }
1152 : : else
1153 : : {
1154 : : /*
1155 : : * else we just use the recoveryTargetTLI as already read from
1156 : : * ControlFile
1157 : : */
1723 heikki.linnakangas@i 1158 [ # # ]:UBC 0 : Assert(recoveryTargetTimeLineGoal == RECOVERY_TARGET_TIMELINE_CONTROLFILE);
1159 : : }
1160 : : }
1161 : :
1162 : : /*
1163 : : * read_backup_label: check to see if a backup_label file is present
1164 : : *
1165 : : * If we see a backup_label during recovery, we assume that we are recovering
1166 : : * from a backup dump file, and we therefore roll forward from the checkpoint
1167 : : * identified by the label file, NOT what pg_control says. This avoids the
1168 : : * problem that pg_control might have been archived one or more checkpoints
1169 : : * later than the start of the dump, and so if we rely on it as the start
1170 : : * point, we will fail to restore a consistent database state.
1171 : : *
1172 : : * Returns true if a backup_label was found (and fills the checkpoint
1173 : : * location and TLI into *checkPointLoc and *backupLabelTLI, respectively);
1174 : : * returns false if not. If this backup_label came from a streamed backup,
1175 : : * *backupEndRequired is set to true. If this backup_label was created during
1176 : : * recovery, *backupFromStandby is set to true.
1177 : : *
1178 : : * Also sets the global variables RedoStartLSN and RedoStartTLI with the LSN
1179 : : * and TLI read from the backup file.
1180 : : */
1181 : : static bool
1723 heikki.linnakangas@i 1182 :CBC 1131 : read_backup_label(XLogRecPtr *checkPointLoc, TimeLineID *backupLabelTLI,
1183 : : bool *backupEndRequired, bool *backupFromStandby)
1184 : : {
1185 : : char startxlogfilename[MAXFNAMELEN];
1186 : : TimeLineID tli_from_walseg,
1187 : : tli_from_file;
1188 : : FILE *lfp;
1189 : : char ch;
1190 : : char backuptype[20];
1191 : : char backupfrom[20];
1192 : : char backuplabel[MAXPGPATH];
1193 : : char backuptime[128];
1194 : : char lsn[MAXPG_LSNLEN + 1];
1195 : :
1196 : : /* suppress possible uninitialized-variable warnings */
1197 : 1131 : *checkPointLoc = InvalidXLogRecPtr;
1198 : 1131 : *backupLabelTLI = 0;
1199 : 1131 : *backupEndRequired = false;
1200 : 1131 : *backupFromStandby = false;
1201 : :
1202 : : /*
1203 : : * See if label file is present
1204 : : */
1205 : 1131 : lfp = AllocateFile(BACKUP_LABEL_FILE, "r");
1206 [ + + ]: 1131 : if (!lfp)
1207 : : {
1208 [ - + ]: 1027 : if (errno != ENOENT)
1723 heikki.linnakangas@i 1209 [ # # ]:UBC 0 : ereport(FATAL,
1210 : : (errcode_for_file_access(),
1211 : : errmsg("could not read file \"%s\": %m",
1212 : : BACKUP_LABEL_FILE)));
1723 heikki.linnakangas@i 1213 :CBC 1027 : return false; /* it's not there, all is fine */
1214 : : }
1215 : :
1216 : : /*
1217 : : * Read and parse the START WAL LOCATION and CHECKPOINT lines (this code
1218 : : * is pretty crude, but we are not expecting any variability in the file
1219 : : * format).
1220 : : */
7 fujii@postgresql.org 1221 [ + - ]:GNC 104 : if (fscanf(lfp,
1222 : : "START WAL LOCATION: %" CppAsString2(MAXPG_LSNLEN) "[0123456789ABCDEFabcdef/] (file %08X%16s)%c",
1223 : 104 : lsn, &tli_from_walseg, startxlogfilename, &ch) != 4 ||
1224 [ + - ]: 104 : ch != '\n' ||
1225 [ - + ]: 104 : !pg_parse_lsn(lsn, &RedoStartLSN))
1723 heikki.linnakangas@i 1226 [ # # ]:UBC 0 : ereport(FATAL,
1227 : : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
1228 : : errmsg("invalid data in file \"%s\"", BACKUP_LABEL_FILE)));
1723 heikki.linnakangas@i 1229 :CBC 104 : RedoStartTLI = tli_from_walseg;
7 fujii@postgresql.org 1230 [ + - ]:GNC 104 : if (fscanf(lfp,
1231 : : "CHECKPOINT LOCATION: %" CppAsString2(MAXPG_LSNLEN) "[0123456789ABCDEFabcdef/]%c", lsn,
1232 : 104 : &ch) != 2 ||
1233 [ + - ]: 104 : ch != '\n' ||
1234 [ - + ]: 104 : !pg_parse_lsn(lsn, checkPointLoc))
1723 heikki.linnakangas@i 1235 [ # # ]:UBC 0 : ereport(FATAL,
1236 : : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
1237 : : errmsg("invalid data in file \"%s\"", BACKUP_LABEL_FILE)));
1723 heikki.linnakangas@i 1238 :CBC 104 : *backupLabelTLI = tli_from_walseg;
1239 : :
1240 : : /*
1241 : : * BACKUP METHOD lets us know if this was a typical backup ("streamed",
1242 : : * which could mean either pg_basebackup or the pg_backup_start/stop
1243 : : * method was used) or if this label came from somewhere else (the only
1244 : : * other option today being from pg_rewind). If this was a streamed
1245 : : * backup then we know that we need to play through until we get to the
1246 : : * end of the WAL which was generated during the backup (at which point we
1247 : : * will have reached consistency and backupEndRequired will be reset to be
1248 : : * false).
1249 : : */
1250 [ + - ]: 104 : if (fscanf(lfp, "BACKUP METHOD: %19s\n", backuptype) == 1)
1251 : : {
1252 [ + + ]: 104 : if (strcmp(backuptype, "streamed") == 0)
1253 : 100 : *backupEndRequired = true;
1254 : : }
1255 : :
1256 : : /*
1257 : : * BACKUP FROM lets us know if this was from a primary or a standby. If
1258 : : * it was from a standby, we'll double-check that the control file state
1259 : : * matches that of a standby.
1260 : : */
1261 [ + - ]: 104 : if (fscanf(lfp, "BACKUP FROM: %19s\n", backupfrom) == 1)
1262 : : {
1263 [ + + ]: 104 : if (strcmp(backupfrom, "standby") == 0)
1264 : 10 : *backupFromStandby = true;
1265 : : }
1266 : :
1267 : : /*
1268 : : * Parse START TIME and LABEL. Those are not mandatory fields for recovery
1269 : : * but checking for their presence is useful for debugging and the next
1270 : : * sanity checks. Cope also with the fact that the result buffers have a
1271 : : * pre-allocated size, hence if the backup_label file has been generated
1272 : : * with strings longer than the maximum assumed here an incorrect parsing
1273 : : * happens. That's fine as only minor consistency checks are done
1274 : : * afterwards.
1275 : : */
1276 [ + + ]: 104 : if (fscanf(lfp, "START TIME: %127[^\n]\n", backuptime) == 1)
1277 [ + + ]: 103 : ereport(DEBUG1,
1278 : : (errmsg_internal("backup time %s in file \"%s\"",
1279 : : backuptime, BACKUP_LABEL_FILE)));
1280 : :
1281 [ + + ]: 104 : if (fscanf(lfp, "LABEL: %1023[^\n]\n", backuplabel) == 1)
1282 [ + + ]: 100 : ereport(DEBUG1,
1283 : : (errmsg_internal("backup label %s in file \"%s\"",
1284 : : backuplabel, BACKUP_LABEL_FILE)));
1285 : :
1286 : : /*
1287 : : * START TIMELINE is new as of 11. Its parsing is not mandatory, still use
1288 : : * it as a sanity check if present.
1289 : : */
1290 [ + + ]: 104 : if (fscanf(lfp, "START TIMELINE: %u\n", &tli_from_file) == 1)
1291 : : {
1292 [ - + ]: 100 : if (tli_from_walseg != tli_from_file)
1723 heikki.linnakangas@i 1293 [ # # ]:UBC 0 : ereport(FATAL,
1294 : : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
1295 : : errmsg("invalid data in file \"%s\"", BACKUP_LABEL_FILE),
1296 : : errdetail("Timeline ID parsed is %u, but expected %u.",
1297 : : tli_from_file, tli_from_walseg)));
1298 : :
1723 heikki.linnakangas@i 1299 [ + + ]:CBC 100 : ereport(DEBUG1,
1300 : : (errmsg_internal("backup timeline %u in file \"%s\"",
1301 : : tli_from_file, BACKUP_LABEL_FILE)));
1302 : : }
1303 : :
1304 : : /*
1305 : : * INCREMENTAL FROM LSN indicates that this is an incremental backup. The
1306 : : * LSN itself need not be parsed, so reading its first character is
1307 : : * sufficient.
1308 : : */
7 fujii@postgresql.org 1309 [ - + ]:GNC 104 : if (fscanf(lfp, "INCREMENTAL FROM LSN: %c", &ch) == 1)
1051 rhaas@postgresql.org 1310 [ # # ]:UBC 0 : ereport(FATAL,
1311 : : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
1312 : : errmsg("this is an incremental backup, not a data directory"),
1313 : : errhint("Use pg_combinebackup to reconstruct a valid data directory.")));
1314 : :
1723 heikki.linnakangas@i 1315 [ + - - + ]:CBC 104 : if (ferror(lfp) || FreeFile(lfp))
1723 heikki.linnakangas@i 1316 [ # # ]:UBC 0 : ereport(FATAL,
1317 : : (errcode_for_file_access(),
1318 : : errmsg("could not read file \"%s\": %m",
1319 : : BACKUP_LABEL_FILE)));
1320 : :
1723 heikki.linnakangas@i 1321 :CBC 104 : return true;
1322 : : }
1323 : :
1324 : : /*
1325 : : * read_tablespace_map: check to see if a tablespace_map file is present
1326 : : *
1327 : : * If we see a tablespace_map file during recovery, we assume that we are
1328 : : * recovering from a backup dump file, and we therefore need to create symlinks
1329 : : * as per the information present in tablespace_map file.
1330 : : *
1331 : : * Returns true if a tablespace_map file was found (and fills *tablespaces
1332 : : * with a tablespaceinfo struct for each tablespace listed in the file);
1333 : : * returns false if not.
1334 : : */
1335 : : static bool
1336 : 102 : read_tablespace_map(List **tablespaces)
1337 : : {
1338 : : tablespaceinfo *ti;
1339 : : FILE *lfp;
1340 : : char str[MAXPGPATH];
1341 : : int ch,
1342 : : i,
1343 : : n;
1344 : : bool was_backslash;
1345 : :
1346 : : /*
1347 : : * See if tablespace_map file is present
1348 : : */
1349 : 102 : lfp = AllocateFile(TABLESPACE_MAP, "r");
1350 [ + + ]: 102 : if (!lfp)
1351 : : {
1352 [ - + ]: 100 : if (errno != ENOENT)
1723 heikki.linnakangas@i 1353 [ # # ]:UBC 0 : ereport(FATAL,
1354 : : (errcode_for_file_access(),
1355 : : errmsg("could not read file \"%s\": %m",
1356 : : TABLESPACE_MAP)));
1723 heikki.linnakangas@i 1357 :CBC 100 : return false; /* it's not there, all is fine */
1358 : : }
1359 : :
1360 : : /*
1361 : : * Read and parse the link name and path lines from tablespace_map file
1362 : : * (this code is pretty crude, but we are not expecting any variability in
1363 : : * the file format). De-escape any backslashes that were inserted.
1364 : : */
1365 : 2 : i = 0;
1366 : 2 : was_backslash = false;
1367 [ + + ]: 77 : while ((ch = fgetc(lfp)) != EOF)
1368 : : {
1369 [ + - + + : 75 : if (!was_backslash && (ch == '\n' || ch == '\r'))
- + ]
1370 : 2 : {
1371 : : char *endp;
1372 : :
1373 [ - + ]: 2 : if (i == 0)
1723 heikki.linnakangas@i 1374 :UBC 0 : continue; /* \r immediately followed by \n */
1375 : :
1376 : : /*
1377 : : * The de-escaped line should contain an OID followed by exactly
1378 : : * one space followed by a path. The path might start with
1379 : : * spaces, so don't be too liberal about parsing.
1380 : : */
1723 heikki.linnakangas@i 1381 :CBC 2 : str[i] = '\0';
1382 : 2 : n = 0;
1383 [ + - + + ]: 12 : while (str[n] && str[n] != ' ')
1384 : 10 : n++;
1385 [ + - - + ]: 2 : if (n < 1 || n >= i - 1)
1723 heikki.linnakangas@i 1386 [ # # ]:UBC 0 : ereport(FATAL,
1387 : : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
1388 : : errmsg("invalid data in file \"%s\"", TABLESPACE_MAP)));
1723 heikki.linnakangas@i 1389 :CBC 2 : str[n++] = '\0';
1390 : :
330 michael@paquier.xyz 1391 : 2 : ti = palloc0_object(tablespaceinfo);
1109 rhaas@postgresql.org 1392 : 2 : errno = 0;
1393 : 2 : ti->oid = strtoul(str, &endp, 10);
1394 [ + - + - : 2 : if (*endp != '\0' || errno == EINVAL || errno == ERANGE)
- + ]
1109 rhaas@postgresql.org 1395 [ # # ]:UBC 0 : ereport(FATAL,
1396 : : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
1397 : : errmsg("invalid data in file \"%s\"", TABLESPACE_MAP)));
1723 heikki.linnakangas@i 1398 :CBC 2 : ti->path = pstrdup(str + n);
1399 : 2 : *tablespaces = lappend(*tablespaces, ti);
1400 : :
1401 : 2 : i = 0;
1402 : 2 : continue;
1403 : : }
1404 [ + - - + ]: 73 : else if (!was_backslash && ch == '\\')
1723 heikki.linnakangas@i 1405 :UBC 0 : was_backslash = true;
1406 : : else
1407 : : {
1723 heikki.linnakangas@i 1408 [ + - ]:CBC 73 : if (i < sizeof(str) - 1)
1409 : 73 : str[i++] = ch;
1410 : 73 : was_backslash = false;
1411 : : }
1412 : : }
1413 : :
1414 [ + - - + ]: 2 : if (i != 0 || was_backslash) /* last line not terminated? */
1723 heikki.linnakangas@i 1415 [ # # ]:UBC 0 : ereport(FATAL,
1416 : : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
1417 : : errmsg("invalid data in file \"%s\"", TABLESPACE_MAP)));
1418 : :
1723 heikki.linnakangas@i 1419 [ + - - + ]:CBC 2 : if (ferror(lfp) || FreeFile(lfp))
1723 heikki.linnakangas@i 1420 [ # # ]:UBC 0 : ereport(FATAL,
1421 : : (errcode_for_file_access(),
1422 : : errmsg("could not read file \"%s\": %m",
1423 : : TABLESPACE_MAP)));
1424 : :
1723 heikki.linnakangas@i 1425 :CBC 2 : return true;
1426 : : }
1427 : :
1428 : : /*
1429 : : * Finish WAL recovery.
1430 : : *
1431 : : * This does not close the 'xlogreader' yet, because in some cases the caller
1432 : : * still wants to re-read the last checkpoint record by calling
1433 : : * ReadCheckpointRecord().
1434 : : *
1435 : : * Returns the position of the last valid or applied record, after which new
1436 : : * WAL should be appended, information about why recovery was ended, and some
1437 : : * other things. See the EndOfWalRecoveryInfo struct for details.
1438 : : */
1439 : : EndOfWalRecoveryInfo *
1440 : 1052 : FinishWalRecovery(void)
1441 : : {
330 michael@paquier.xyz 1442 : 1052 : EndOfWalRecoveryInfo *result = palloc_object(EndOfWalRecoveryInfo);
1443 : : XLogRecPtr lastRec;
1444 : : TimeLineID lastRecTLI;
1445 : : XLogRecPtr endOfLog;
1446 : :
1447 : : /*
1448 : : * Kill WAL receiver, if it's still running, before we continue to write
1449 : : * the startup checkpoint and aborted-contrecord records. It will trump
1450 : : * over these records and subsequent ones if it's still alive when we
1451 : : * start writing WAL.
1452 : : */
1723 heikki.linnakangas@i 1453 : 1052 : XLogShutdownWalRcv();
1454 : :
1455 : : /*
1456 : : * Shutdown the slot sync worker to drop any temporary slots acquired by
1457 : : * it and to prevent it from keep trying to fetch the failover slots.
1458 : : *
1459 : : * We do not update the 'synced' column in 'pg_replication_slots' system
1460 : : * view from true to false here, as any failed update could leave 'synced'
1461 : : * column false for some slots. This could cause issues during slot sync
1462 : : * after restarting the server as a standby. While updating the 'synced'
1463 : : * column after switching to the new timeline is an option, it does not
1464 : : * simplify the handling for the 'synced' column. Therefore, we retain the
1465 : : * 'synced' column as true after promotion as it may provide useful
1466 : : * information about the slot origin.
1467 : : */
987 akapila@postgresql.o 1468 : 1052 : ShutDownSlotSync();
1469 : :
1470 : : /*
1471 : : * We are now done reading the xlog from stream. Turn off streaming
1472 : : * recovery to force fetching the files (which would be required at end of
1473 : : * recovery, e.g., timeline history file) from archive or pg_wal.
1474 : : *
1475 : : * Note that standby mode must be turned off after killing WAL receiver,
1476 : : * i.e., calling XLogShutdownWalRcv().
1477 : : */
1723 heikki.linnakangas@i 1478 [ - + ]: 1052 : Assert(!WalRcvStreaming());
1479 : 1052 : StandbyMode = false;
1480 : :
1481 : : /*
1482 : : * Determine where to start writing WAL next.
1483 : : *
1484 : : * Re-fetch the last valid or last applied record, so we can identify the
1485 : : * exact endpoint of what we consider the valid portion of WAL. There may
1486 : : * be an incomplete continuation record after that, in which case
1487 : : * 'abortedRecPtr' and 'missingContrecPtr' are set and the caller will
1488 : : * write a special OVERWRITE_CONTRECORD message to mark that the rest of
1489 : : * it is intentionally missing. See CreateOverwriteContrecordRecord().
1490 : : *
1491 : : * An important side-effect of this is to load the last page into
1492 : : * xlogreader. The caller uses it to initialize the WAL for writing.
1493 : : */
1494 [ + + ]: 1052 : if (!InRecovery)
1495 : : {
1496 : 871 : lastRec = CheckPointLoc;
1497 : 871 : lastRecTLI = CheckPointTLI;
1498 : : }
1499 : : else
1500 : : {
1501 : 181 : lastRec = XLogRecoveryCtl->lastReplayedReadRecPtr;
1502 : 181 : lastRecTLI = XLogRecoveryCtl->lastReplayedTLI;
1503 : : }
1673 tmunro@postgresql.or 1504 : 1052 : XLogPrefetcherBeginRead(xlogprefetcher, lastRec);
1505 : 1052 : (void) ReadRecord(xlogprefetcher, PANIC, false, lastRecTLI);
1723 heikki.linnakangas@i 1506 : 1052 : endOfLog = xlogreader->EndRecPtr;
1507 : :
1508 : : /*
1509 : : * Remember the TLI in the filename of the XLOG segment containing the
1510 : : * end-of-log. It could be different from the timeline that endOfLog
1511 : : * nominally belongs to, if there was a timeline switch in that segment,
1512 : : * and we were reading the old WAL from a segment belonging to a higher
1513 : : * timeline.
1514 : : */
1515 : 1052 : result->endOfLogTLI = xlogreader->seg.ws_tli;
1516 : :
1517 [ + + ]: 1052 : if (ArchiveRecoveryRequested)
1518 : : {
1519 : : /*
1520 : : * We are no longer in archive recovery state.
1521 : : *
1522 : : * We are now done reading the old WAL. Turn off archive fetching if
1523 : : * it was active.
1524 : : */
1525 [ - + ]: 66 : Assert(InArchiveRecovery);
1526 : 66 : InArchiveRecovery = false;
1527 : :
1528 : : /*
1529 : : * If the ending log segment is still open, close it (to avoid
1530 : : * problems on Windows with trying to rename or delete an open file).
1531 : : */
1532 [ + - ]: 66 : if (readFile >= 0)
1533 : : {
1534 : 66 : close(readFile);
1535 : 66 : readFile = -1;
1536 : : }
1537 : : }
1538 : :
1539 : : /*
1540 : : * Copy the last partial block to the caller, for initializing the WAL
1541 : : * buffer for appending new WAL.
1542 : : */
1543 [ + + ]: 1052 : if (endOfLog % XLOG_BLCKSZ != 0)
1544 : : {
1545 : : char *page;
1546 : : int len;
1547 : : XLogRecPtr pageBeginPtr;
1548 : :
1549 : 1031 : pageBeginPtr = endOfLog - (endOfLog % XLOG_BLCKSZ);
1550 [ - + ]: 1031 : Assert(readOff == XLogSegmentOffset(pageBeginPtr, wal_segment_size));
1551 : :
1552 : : /* Copy the valid part of the last block */
1553 : 1031 : len = endOfLog % XLOG_BLCKSZ;
1554 : 1031 : page = palloc(len);
1555 : 1031 : memcpy(page, xlogreader->readBuf, len);
1556 : :
1557 : 1031 : result->lastPageBeginPtr = pageBeginPtr;
1558 : 1031 : result->lastPage = page;
1559 : : }
1560 : : else
1561 : : {
1562 : : /* There is no partial block to copy. */
1563 : 21 : result->lastPageBeginPtr = endOfLog;
1564 : 21 : result->lastPage = NULL;
1565 : : }
1566 : :
1567 : : /*
1568 : : * Create a comment for the history file to explain why and where timeline
1569 : : * changed.
1570 : : */
1571 : 1052 : result->recoveryStopReason = getRecoveryStopReason();
1572 : :
1573 : 1052 : result->lastRec = lastRec;
1574 : 1052 : result->lastRecTLI = lastRecTLI;
1575 : 1052 : result->endOfLog = endOfLog;
1576 : :
1577 : 1052 : result->abortedRecPtr = abortedRecPtr;
1578 : 1052 : result->missingContrecPtr = missingContrecPtr;
1579 : :
1580 : 1052 : result->standby_signal_file_found = standby_signal_file_found;
1581 : 1052 : result->recovery_signal_file_found = recovery_signal_file_found;
1582 : :
1583 : 1052 : return result;
1584 : : }
1585 : :
1586 : : /*
1587 : : * Clean up the WAL reader and leftovers from restoring WAL from archive
1588 : : */
1589 : : void
1590 : 1052 : ShutdownWalRecovery(void)
1591 : : {
1592 : : char recoveryPath[MAXPGPATH];
1593 : :
1594 : : /* Final update of pg_stat_recovery_prefetch. */
1673 tmunro@postgresql.or 1595 : 1052 : XLogPrefetcherComputeStats(xlogprefetcher);
1596 : :
1597 : : /* Shut down xlogreader */
1723 heikki.linnakangas@i 1598 [ + + ]: 1052 : if (readFile >= 0)
1599 : : {
1600 : 986 : close(readFile);
1601 : 986 : readFile = -1;
1602 : : }
460 tgl@sss.pgh.pa.us 1603 : 1052 : pfree(xlogreader->private_data);
1723 heikki.linnakangas@i 1604 : 1052 : XLogReaderFree(xlogreader);
1673 tmunro@postgresql.or 1605 : 1052 : XLogPrefetcherFree(xlogprefetcher);
1606 : :
1723 heikki.linnakangas@i 1607 [ + + ]: 1052 : if (ArchiveRecoveryRequested)
1608 : : {
1609 : : /*
1610 : : * Since there might be a partial WAL segment named RECOVERYXLOG, get
1611 : : * rid of it.
1612 : : */
1613 : 66 : snprintf(recoveryPath, MAXPGPATH, XLOGDIR "/RECOVERYXLOG");
1614 : 66 : unlink(recoveryPath); /* ignore any error */
1615 : :
1616 : : /* Get rid of any remaining recovered timeline-history file, too */
1617 : 66 : snprintf(recoveryPath, MAXPGPATH, XLOGDIR "/RECOVERYHISTORY");
1618 : 66 : unlink(recoveryPath); /* ignore any error */
1619 : : }
1620 : :
1621 : : /*
1622 : : * We don't need the latch anymore. It's not strictly necessary to disown
1623 : : * it, but let's do it for the sake of tidiness.
1624 : : */
1625 [ + + ]: 1052 : if (ArchiveRecoveryRequested)
1626 : 66 : DisownLatch(&XLogRecoveryCtl->recoveryWakeupLatch);
1627 : 1052 : }
1628 : :
1629 : : /*
1630 : : * Perform WAL recovery.
1631 : : *
1632 : : * If the system was shut down cleanly, this is never called.
1633 : : */
1634 : : void
1635 : 254 : PerformWalRecovery(void)
1636 : : {
1637 : : XLogRecord *record;
1638 : 254 : bool reachedRecoveryTarget = false;
1639 : : TimeLineID replayTLI;
1640 : :
1641 : : /*
1642 : : * Initialize shared variables for tracking progress of WAL replay, as if
1643 : : * we had just replayed the record before the REDO location (or the
1644 : : * checkpoint record itself, if it's a shutdown checkpoint).
1645 : : */
1646 : 254 : SpinLockAcquire(&XLogRecoveryCtl->info_lck);
1647 [ + + ]: 254 : if (RedoStartLSN < CheckPointLoc)
1648 : : {
1649 : 145 : XLogRecoveryCtl->lastReplayedReadRecPtr = InvalidXLogRecPtr;
1650 : 145 : XLogRecoveryCtl->lastReplayedEndRecPtr = RedoStartLSN;
1651 : 145 : XLogRecoveryCtl->lastReplayedTLI = RedoStartTLI;
1652 : : }
1653 : : else
1654 : : {
1655 : 109 : XLogRecoveryCtl->lastReplayedReadRecPtr = xlogreader->ReadRecPtr;
1656 : 109 : XLogRecoveryCtl->lastReplayedEndRecPtr = xlogreader->EndRecPtr;
1657 : 109 : XLogRecoveryCtl->lastReplayedTLI = CheckPointTLI;
1658 : : }
1659 : 254 : XLogRecoveryCtl->replayEndRecPtr = XLogRecoveryCtl->lastReplayedEndRecPtr;
1660 : 254 : XLogRecoveryCtl->replayEndTLI = XLogRecoveryCtl->lastReplayedTLI;
1661 : 254 : XLogRecoveryCtl->recoveryLastXTime = 0;
1662 : 254 : XLogRecoveryCtl->currentChunkStartTime = 0;
1663 : 254 : XLogRecoveryCtl->recoveryPauseState = RECOVERY_NOT_PAUSED;
1664 : 254 : SpinLockRelease(&XLogRecoveryCtl->info_lck);
1665 : :
1666 : : /* Also ensure XLogReceiptTime has a sane value */
1667 : 254 : XLogReceiptTime = GetCurrentTimestamp();
1668 : :
1669 : : /*
1670 : : * Let postmaster know we've started redo now, so that it can launch the
1671 : : * archiver if necessary.
1672 : : */
1673 [ + + ]: 254 : if (IsUnderPostmaster)
1674 : 245 : SendPostmasterSignal(PMSIGNAL_RECOVERY_STARTED);
1675 : :
1676 : : /*
1677 : : * Allow read-only connections immediately if we're consistent already.
1678 : : */
1679 : 254 : CheckRecoveryConsistency();
1680 : :
1681 : : /*
1682 : : * Find the first record that logically follows the checkpoint --- it
1683 : : * might physically precede it, though.
1684 : : */
1685 [ + + ]: 254 : if (RedoStartLSN < CheckPointLoc)
1686 : : {
1687 : : /* back up to find the record */
1688 : 145 : replayTLI = RedoStartTLI;
1673 tmunro@postgresql.or 1689 : 145 : XLogPrefetcherBeginRead(xlogprefetcher, RedoStartLSN);
1690 : 145 : record = ReadRecord(xlogprefetcher, PANIC, false, replayTLI);
1691 : :
1692 : : /*
1693 : : * If a checkpoint record's redo pointer points back to an earlier
1694 : : * LSN, the record at that LSN should be an XLOG_CHECKPOINT_REDO
1695 : : * record.
1696 : : */
1113 rhaas@postgresql.org 1697 [ + - ]: 145 : if (record->xl_rmid != RM_XLOG_ID ||
1698 [ - + ]: 145 : (record->xl_info & ~XLR_INFO_MASK) != XLOG_CHECKPOINT_REDO)
1113 rhaas@postgresql.org 1699 [ # # ]:UBC 0 : ereport(FATAL,
1700 : : errmsg("unexpected record type found at redo point %X/%08X",
1701 : : LSN_FORMAT_ARGS(xlogreader->ReadRecPtr)));
1702 : : }
1703 : : else
1704 : : {
1705 : : /* just have to read next record after CheckPoint */
1723 heikki.linnakangas@i 1706 [ - + ]:CBC 109 : Assert(xlogreader->ReadRecPtr == CheckPointLoc);
1707 : 109 : replayTLI = CheckPointTLI;
1673 tmunro@postgresql.or 1708 : 109 : record = ReadRecord(xlogprefetcher, LOG, false, replayTLI);
1709 : : }
1710 : :
1723 heikki.linnakangas@i 1711 [ + + ]: 254 : if (record != NULL)
1712 : : {
1713 : : TimestampTz xtime;
1714 : : PGRUsage ru0;
1715 : :
1716 : 242 : pg_rusage_init(&ru0);
1717 : :
1718 : 242 : InRedo = true;
1719 : :
1674 jdavis@postgresql.or 1720 : 242 : RmgrStartup();
1721 : :
1723 heikki.linnakangas@i 1722 [ + - ]: 242 : ereport(LOG,
1723 : : errmsg("redo starts at %X/%08X",
1724 : : LSN_FORMAT_ARGS(xlogreader->ReadRecPtr)));
1725 : :
1726 : : /* Prepare to report progress of the redo phase. */
1727 [ + + ]: 242 : if (!StandbyMode)
1728 : 113 : begin_startup_progress_phase();
1729 : :
1730 : : /*
1731 : : * main redo apply loop
1732 : : */
1733 : : do
1734 : : {
1735 [ + + ]: 2941302 : if (!StandbyMode)
486 alvherre@kurilemu.de 1736 [ - + - - ]: 327664 : ereport_startup_progress("redo in progress, elapsed time: %ld.%02d s, current LSN: %X/%08X",
1737 : : LSN_FORMAT_ARGS(xlogreader->ReadRecPtr));
1738 : :
1739 : : #ifdef WAL_DEBUG
1740 : : if (XLOG_DEBUG)
1741 : : {
1742 : : StringInfoData buf;
1743 : :
1744 : : initStringInfo(&buf);
1745 : : appendStringInfo(&buf, "REDO @ %X/%08X; LSN %X/%08X: ",
1746 : : LSN_FORMAT_ARGS(xlogreader->ReadRecPtr),
1747 : : LSN_FORMAT_ARGS(xlogreader->EndRecPtr));
1748 : : xlog_outrec(&buf, xlogreader);
1749 : : appendStringInfoString(&buf, " - ");
1750 : : xlog_outdesc(&buf, xlogreader);
1751 : : elog(LOG, "%s", buf.data);
1752 : : pfree(buf.data);
1753 : : }
1754 : : #endif
1755 : :
1756 : : /* Handle interrupt signals of startup process */
610 heikki.linnakangas@i 1757 : 2941302 : ProcessStartupProcInterrupts();
1758 : :
1759 : : /*
1760 : : * Pause WAL replay, if requested by a hot-standby session via
1761 : : * SetRecoveryPause().
1762 : : *
1763 : : * Note that we intentionally don't take the info_lck spinlock
1764 : : * here. We might therefore read a slightly stale value of the
1765 : : * recoveryPause flag, but it can't be very stale (no worse than
1766 : : * the last spinlock we did acquire). Since a pause request is a
1767 : : * pretty asynchronous thing anyway, possibly responding to it one
1768 : : * WAL record later than we otherwise would is a minor issue, so
1769 : : * it doesn't seem worth adding another spinlock cycle to prevent
1770 : : * that.
1771 : : */
1723 1772 [ - + ]: 2941302 : if (((volatile XLogRecoveryCtlData *) XLogRecoveryCtl)->recoveryPauseState !=
1773 : : RECOVERY_NOT_PAUSED)
1723 heikki.linnakangas@i 1774 :UBC 0 : recoveryPausesHere(false);
1775 : :
1776 : : /*
1777 : : * Have we reached our recovery target?
1778 : : */
1723 heikki.linnakangas@i 1779 [ + + ]:CBC 2941302 : if (recoveryStopsBefore(xlogreader))
1780 : : {
1781 : 2 : reachedRecoveryTarget = true;
1782 : 2 : break;
1783 : : }
1784 : :
1785 : : /*
1786 : : * If we've been asked to lag the primary, wait on latch until
1787 : : * enough time has passed.
1788 : : */
1789 [ + + ]: 2941300 : if (recoveryApplyDelay(xlogreader))
1790 : : {
1791 : : /*
1792 : : * We test for paused recovery again here. If user sets
1793 : : * delayed apply, it may be because they expect to pause
1794 : : * recovery in case of problems, so we must test again here
1795 : : * otherwise pausing during the delay-wait wouldn't work.
1796 : : */
1797 [ + + ]: 25 : if (((volatile XLogRecoveryCtlData *) XLogRecoveryCtl)->recoveryPauseState !=
1798 : : RECOVERY_NOT_PAUSED)
1799 : 1 : recoveryPausesHere(false);
1800 : : }
1801 : :
1802 : : /*
1803 : : * Apply the record
1804 : : */
1805 : 2941300 : ApplyWalRecord(xlogreader, record, &replayTLI);
1806 : :
1807 : : /*
1808 : : * Wake up processes waiting for standby replay, write, or flush
1809 : : * LSN to reach current replay position. Replay implies that the
1810 : : * WAL was already written and flushed to disk, so write and flush
1811 : : * waiters can be woken at the replay position too.
1812 : : */
186 akorotkov@postgresql 1813 : 2941298 : WaitLSNWakeup(WAIT_LSN_TYPE_STANDBY_REPLAY,
1814 : 2941298 : XLogRecoveryCtl->lastReplayedEndRecPtr);
1815 : 2941298 : WaitLSNWakeup(WAIT_LSN_TYPE_STANDBY_WRITE,
1816 : 2941298 : XLogRecoveryCtl->lastReplayedEndRecPtr);
1817 : 2941298 : WaitLSNWakeup(WAIT_LSN_TYPE_STANDBY_FLUSH,
1818 : 2941298 : XLogRecoveryCtl->lastReplayedEndRecPtr);
1819 : :
1820 : : /* Exit loop if we reached inclusive recovery target */
280 1821 [ + + ]: 2941298 : if (recoveryStopsAfter(xlogreader))
1822 : : {
1823 : 5 : reachedRecoveryTarget = true;
1824 : 5 : break;
1825 : : }
1826 : :
1827 : : /* Else, try to fetch the next WAL record */
1673 tmunro@postgresql.or 1828 : 2941293 : record = ReadRecord(xlogprefetcher, LOG, false, replayTLI);
1723 heikki.linnakangas@i 1829 [ + + ]: 2941223 : } while (record != NULL);
1830 : :
1831 : : /*
1832 : : * end of main redo apply loop
1833 : : */
1834 : :
1835 [ + + ]: 170 : if (reachedRecoveryTarget)
1836 : : {
1837 [ - + ]: 7 : if (!reachedConsistency)
1723 heikki.linnakangas@i 1838 [ # # ]:UBC 0 : ereport(FATAL,
1839 : : (errmsg("requested recovery stop point is before consistent recovery point")));
1840 : :
1841 : : /*
1842 : : * This is the last point where we can restart recovery with a new
1843 : : * recovery target, if we shutdown and begin again. After this,
1844 : : * Resource Managers may choose to do permanent corrective actions
1845 : : * at end of recovery.
1846 : : */
1723 heikki.linnakangas@i 1847 [ - + + - ]:CBC 7 : switch (recoveryTargetAction)
1848 : : {
1723 heikki.linnakangas@i 1849 :UBC 0 : case RECOVERY_TARGET_ACTION_SHUTDOWN:
1850 : :
1851 : : /*
1852 : : * exit with special return code to request shutdown of
1853 : : * postmaster. Log messages issued from postmaster.
1854 : : */
1855 : 0 : proc_exit(3);
1856 : :
1723 heikki.linnakangas@i 1857 :CBC 1 : case RECOVERY_TARGET_ACTION_PAUSE:
1858 : 1 : SetRecoveryPause(true);
1859 : 1 : recoveryPausesHere(true);
1860 : :
1861 : : /* drop into promote */
1862 : : pg_fallthrough;
1863 : :
1864 : 7 : case RECOVERY_TARGET_ACTION_PROMOTE:
1865 : 7 : break;
1866 : : }
1867 : : }
1868 : :
1674 jdavis@postgresql.or 1869 : 170 : RmgrCleanup();
1870 : :
1723 heikki.linnakangas@i 1871 [ + - ]: 170 : ereport(LOG,
1872 : : errmsg("redo done at %X/%08X system usage: %s",
1873 : : LSN_FORMAT_ARGS(xlogreader->ReadRecPtr),
1874 : : pg_rusage_show(&ru0)));
1875 : 170 : xtime = GetLatestXTime();
1876 [ + + ]: 170 : if (xtime)
1877 [ + - ]: 47 : ereport(LOG,
1878 : : (errmsg("last completed transaction was at log time %s",
1879 : : timestamptz_to_str(xtime))));
1880 : :
1881 : 170 : InRedo = false;
1882 : : }
1883 : : else
1884 : : {
1885 : : /* there are no WAL records following the checkpoint */
1886 [ + - ]: 12 : ereport(LOG,
1887 : : (errmsg("redo is not required")));
1888 : : }
1889 : :
1890 : : /*
1891 : : * This check is intentionally after the above log messages that indicate
1892 : : * how far recovery went.
1893 : : */
1894 [ + + ]: 182 : if (ArchiveRecoveryRequested &&
1895 [ + + ]: 67 : recoveryTarget != RECOVERY_TARGET_UNSET &&
1896 [ + + ]: 8 : !reachedRecoveryTarget)
1897 [ + - ]: 1 : ereport(FATAL,
1898 : : (errcode(ERRCODE_CONFIG_FILE_ERROR),
1899 : : errmsg("recovery ended before configured recovery target was reached")));
1900 : 181 : }
1901 : :
1902 : : /*
1903 : : * Subroutine of PerformWalRecovery, to apply one WAL record.
1904 : : */
1905 : : static void
1906 : 2941300 : ApplyWalRecord(XLogReaderState *xlogreader, XLogRecord *record, TimeLineID *replayTLI)
1907 : : {
1908 : : ErrorContextCallback errcallback;
1909 : 2941300 : bool switchedTLI = false;
1910 : :
1911 : : /* Setup error traceback support for ereport() */
1912 : 2941300 : errcallback.callback = rm_redo_error_callback;
707 peter@eisentraut.org 1913 : 2941300 : errcallback.arg = xlogreader;
1723 heikki.linnakangas@i 1914 : 2941300 : errcallback.previous = error_context_stack;
1915 : 2941300 : error_context_stack = &errcallback;
1916 : :
1917 : : /*
1918 : : * TransamVariables->nextXid must be beyond record's xid.
1919 : : */
1920 : 2941300 : AdvanceNextFullTransactionIdPastXid(record->xl_xid);
1921 : :
1922 : : /*
1923 : : * Before replaying this record, check if this record causes the current
1924 : : * timeline to change. The record is already considered to be part of the
1925 : : * new timeline, so we update replayTLI before replaying it. That's
1926 : : * important so that replayEndTLI, which is recorded as the minimum
1927 : : * recovery point's TLI if recovery stops after this record, is set
1928 : : * correctly.
1929 : : */
1930 [ + + ]: 2941300 : if (record->xl_rmid == RM_XLOG_ID)
1931 : : {
1932 : 119237 : TimeLineID newReplayTLI = *replayTLI;
1933 : 119237 : TimeLineID prevReplayTLI = *replayTLI;
1934 : 119237 : uint8 info = record->xl_info & ~XLR_INFO_MASK;
1935 : :
1936 [ + + ]: 119237 : if (info == XLOG_CHECKPOINT_SHUTDOWN)
1937 : : {
1938 : : CheckPoint checkPoint;
1939 : :
1940 : 50 : memcpy(&checkPoint, XLogRecGetData(xlogreader), sizeof(CheckPoint));
1941 : 50 : newReplayTLI = checkPoint.ThisTimeLineID;
1942 : 50 : prevReplayTLI = checkPoint.PrevTimeLineID;
1943 : : }
1944 [ + + ]: 119187 : else if (info == XLOG_END_OF_RECOVERY)
1945 : : {
1946 : : xl_end_of_recovery xlrec;
1947 : :
1948 : 14 : memcpy(&xlrec, XLogRecGetData(xlogreader), sizeof(xl_end_of_recovery));
1949 : 14 : newReplayTLI = xlrec.ThisTimeLineID;
1950 : 14 : prevReplayTLI = xlrec.PrevTimeLineID;
1951 : : }
1952 : :
1953 [ + + ]: 119237 : if (newReplayTLI != *replayTLI)
1954 : : {
1955 : : /* Check that it's OK to switch to this TLI */
1956 : 15 : checkTimeLineSwitch(xlogreader->EndRecPtr,
1957 : : newReplayTLI, prevReplayTLI, *replayTLI);
1958 : :
1959 : : /* Following WAL records should be run with new TLI */
1960 : 15 : *replayTLI = newReplayTLI;
1961 : 15 : switchedTLI = true;
1962 : : }
1963 : : }
1964 : :
1965 : : /*
1966 : : * Update shared replayEndRecPtr before replaying this record, so that
1967 : : * XLogFlush will update minRecoveryPoint correctly.
1968 : : */
1969 : 2941300 : SpinLockAcquire(&XLogRecoveryCtl->info_lck);
1970 : 2941300 : XLogRecoveryCtl->replayEndRecPtr = xlogreader->EndRecPtr;
1971 : 2941300 : XLogRecoveryCtl->replayEndTLI = *replayTLI;
1972 : 2941300 : SpinLockRelease(&XLogRecoveryCtl->info_lck);
1973 : :
1974 : : /*
1975 : : * If we are attempting to enter Hot Standby mode, process XIDs we see
1976 : : */
1977 [ + + ]: 2941300 : if (standbyState >= STANDBY_INITIALIZED &&
1978 [ + + ]: 2633490 : TransactionIdIsValid(record->xl_xid))
1979 : 2566500 : RecordKnownAssignedTransactionIds(record->xl_xid);
1980 : :
1981 : : /*
1982 : : * Some XLOG record types that are related to recovery are processed
1983 : : * directly here, rather than in xlog_redo()
1984 : : */
1985 [ + + ]: 2941300 : if (record->xl_rmid == RM_XLOG_ID)
1986 : 119237 : xlogrecovery_redo(xlogreader, *replayTLI);
1987 : :
1988 : : /* Now apply the WAL record itself */
1674 jdavis@postgresql.or 1989 : 2941300 : GetRmgr(record->xl_rmid).rm_redo(xlogreader);
1990 : :
1991 : : /*
1992 : : * After redo, check whether the backup pages associated with the WAL
1993 : : * record are consistent with the existing pages. This check is done only
1994 : : * if consistency check is enabled for this record.
1995 : : */
1723 heikki.linnakangas@i 1996 [ + + ]: 2941298 : if ((record->xl_info & XLR_CHECK_CONSISTENCY) != 0)
1997 : 2246999 : verifyBackupPageConsistency(xlogreader);
1998 : :
1999 : : /* Pop the error context stack */
2000 : 2941298 : error_context_stack = errcallback.previous;
2001 : :
2002 : : /*
2003 : : * Update lastReplayedEndRecPtr after this record has been successfully
2004 : : * replayed.
2005 : : */
2006 : 2941298 : SpinLockAcquire(&XLogRecoveryCtl->info_lck);
2007 : 2941298 : XLogRecoveryCtl->lastReplayedReadRecPtr = xlogreader->ReadRecPtr;
2008 : 2941298 : XLogRecoveryCtl->lastReplayedEndRecPtr = xlogreader->EndRecPtr;
2009 : 2941298 : XLogRecoveryCtl->lastReplayedTLI = *replayTLI;
2010 : 2941298 : SpinLockRelease(&XLogRecoveryCtl->info_lck);
2011 : :
2012 : : /* ------
2013 : : * Wakeup walsenders:
2014 : : *
2015 : : * On the standby, the WAL is flushed first (which will only wake up
2016 : : * physical walsenders) and then applied, which will only wake up logical
2017 : : * walsenders.
2018 : : *
2019 : : * Indeed, logical walsenders on standby can't decode and send data until
2020 : : * it's been applied.
2021 : : *
2022 : : * Physical walsenders don't need to be woken up during replay unless
2023 : : * cascading replication is allowed and time line change occurred (so that
2024 : : * they can notice that they are on a new time line).
2025 : : *
2026 : : * That's why the wake up conditions are for:
2027 : : *
2028 : : * - physical walsenders in case of new time line and cascade
2029 : : * replication is allowed
2030 : : * - logical walsenders in case cascade replication is allowed (could not
2031 : : * be created otherwise)
2032 : : * ------
2033 : : */
1307 andres@anarazel.de 2034 [ + + + + ]: 2941298 : if (AllowCascadeReplication())
2035 : 2689323 : WalSndWakeup(switchedTLI, true);
2036 : :
2037 : : /*
2038 : : * If rm_redo called XLogRequestWalReceiverReply, then we wake up the
2039 : : * receiver so that it notices the updated lastReplayedEndRecPtr and sends
2040 : : * a reply to the primary.
2041 : : */
1723 heikki.linnakangas@i 2042 [ + + ]: 2941298 : if (doRequestWalReceiverReply)
2043 : : {
2044 : 3 : doRequestWalReceiverReply = false;
224 fujii@postgresql.org 2045 : 3 : WalRcvRequestApplyReply();
2046 : : }
2047 : :
2048 : : /* Allow read-only connections if we're consistent now */
1723 heikki.linnakangas@i 2049 : 2941298 : CheckRecoveryConsistency();
2050 : :
2051 : : /* Is this a timeline switch? */
2052 [ + + ]: 2941298 : if (switchedTLI)
2053 : : {
2054 : : /*
2055 : : * Before we continue on the new timeline, clean up any (possibly
2056 : : * bogus) future WAL segments on the old timeline.
2057 : : */
2058 : 15 : RemoveNonParentXlogFiles(xlogreader->EndRecPtr, *replayTLI);
2059 : :
2060 : : /* Reset the prefetcher. */
1673 tmunro@postgresql.or 2061 : 15 : XLogPrefetchReconfigure();
2062 : : }
1723 heikki.linnakangas@i 2063 : 2941298 : }
2064 : :
2065 : : /*
2066 : : * Some XLOG RM record types that are directly related to WAL recovery are
2067 : : * handled here rather than in the xlog_redo()
2068 : : */
2069 : : static void
2070 : 119237 : xlogrecovery_redo(XLogReaderState *record, TimeLineID replayTLI)
2071 : : {
2072 : 119237 : uint8 info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
2073 : 119237 : XLogRecPtr lsn = record->EndRecPtr;
2074 : :
2075 [ - + ]: 119237 : Assert(XLogRecGetRmid(record) == RM_XLOG_ID);
2076 : :
2077 [ + + ]: 119237 : if (info == XLOG_OVERWRITE_CONTRECORD)
2078 : : {
2079 : : /* Verify the payload of a XLOG_OVERWRITE_CONTRECORD record. */
2080 : : xl_overwrite_contrecord xlrec;
2081 : :
2082 : 1 : memcpy(&xlrec, XLogRecGetData(record), sizeof(xl_overwrite_contrecord));
2083 [ - + ]: 1 : if (xlrec.overwritten_lsn != record->overwrittenRecPtr)
486 alvherre@kurilemu.de 2084 [ # # ]:UBC 0 : elog(FATAL, "mismatching overwritten LSN %X/%08X -> %X/%08X",
2085 : : LSN_FORMAT_ARGS(xlrec.overwritten_lsn),
2086 : : LSN_FORMAT_ARGS(record->overwrittenRecPtr));
2087 : :
2088 : : /* We have safely skipped the aborted record */
1688 alvherre@alvh.no-ip. 2089 :CBC 1 : abortedRecPtr = InvalidXLogRecPtr;
2090 : 1 : missingContrecPtr = InvalidXLogRecPtr;
2091 : :
1723 heikki.linnakangas@i 2092 [ + - ]: 1 : ereport(LOG,
2093 : : errmsg("successfully skipped missing contrecord at %X/%08X, overwritten at %s",
2094 : : LSN_FORMAT_ARGS(xlrec.overwritten_lsn),
2095 : : timestamptz_to_str(xlrec.overwrite_time)));
2096 : :
2097 : : /* Verifying the record should only happen once */
2098 : 1 : record->overwrittenRecPtr = InvalidXLogRecPtr;
2099 : : }
2100 [ + + ]: 119236 : else if (info == XLOG_BACKUP_END)
2101 : : {
2102 : : XLogRecPtr startpoint;
2103 : :
2104 : 116 : memcpy(&startpoint, XLogRecGetData(record), sizeof(startpoint));
2105 : :
2106 [ + + ]: 116 : if (backupStartPoint == startpoint)
2107 : : {
2108 : : /*
2109 : : * We have reached the end of base backup, the point where
2110 : : * pg_backup_stop() was done. The data on disk is now consistent
2111 : : * (assuming we have also reached minRecoveryPoint). Set
2112 : : * backupEndPoint to the current LSN, so that the next call to
2113 : : * CheckRecoveryConsistency() will notice it and do the
2114 : : * end-of-backup processing.
2115 : : */
2116 [ + + ]: 97 : elog(DEBUG1, "end of backup record reached");
2117 : :
2118 : 97 : backupEndPoint = lsn;
2119 : : }
2120 : : else
486 alvherre@kurilemu.de 2121 [ + + ]: 19 : elog(DEBUG1, "saw end-of-backup record for backup starting at %X/%08X, waiting for %X/%08X",
2122 : : LSN_FORMAT_ARGS(startpoint), LSN_FORMAT_ARGS(backupStartPoint));
2123 : : }
1723 heikki.linnakangas@i 2124 : 119237 : }
2125 : :
2126 : : /*
2127 : : * Verify that, in non-test mode, ./pg_tblspc doesn't contain any real
2128 : : * directories.
2129 : : *
2130 : : * Replay of database creation XLOG records for databases that were later
2131 : : * dropped can create fake directories in pg_tblspc. By the time consistency
2132 : : * is reached these directories should have been removed; here we verify
2133 : : * that this did indeed happen. This is to be called at the point where
2134 : : * consistent state is reached.
2135 : : *
2136 : : * allow_in_place_tablespaces turns the PANIC into a WARNING, which is
2137 : : * useful for testing purposes, and also allows for an escape hatch in case
2138 : : * things go south.
2139 : : */
2140 : : static void
1561 alvherre@alvh.no-ip. 2141 : 149 : CheckTablespaceDirectory(void)
2142 : : {
2143 : : DIR *dir;
2144 : : struct dirent *de;
2145 : :
793 michael@paquier.xyz 2146 : 149 : dir = AllocateDir(PG_TBLSPC_DIR);
2147 [ + + ]: 454 : while ((de = ReadDir(dir, PG_TBLSPC_DIR)) != NULL)
2148 : : {
2149 : : char path[MAXPGPATH + sizeof(PG_TBLSPC_DIR)];
2150 : :
2151 : : /* Skip entries of non-oid names */
1561 alvherre@alvh.no-ip. 2152 [ + + ]: 305 : if (strspn(de->d_name, "0123456789") != strlen(de->d_name))
2153 : 298 : continue;
2154 : :
793 michael@paquier.xyz 2155 : 7 : snprintf(path, sizeof(path), "%s/%s", PG_TBLSPC_DIR, de->d_name);
2156 : :
1561 alvherre@alvh.no-ip. 2157 [ + + ]: 7 : if (get_dirent_type(path, de, false, ERROR) != PGFILETYPE_LNK)
2158 [ + - + - ]: 4 : ereport(allow_in_place_tablespaces ? WARNING : PANIC,
2159 : : (errcode(ERRCODE_DATA_CORRUPTED),
2160 : : errmsg("unexpected directory entry \"%s\" found in %s",
2161 : : de->d_name, PG_TBLSPC_DIR),
2162 : : errdetail("All directory entries in %s/ should be symbolic links.",
2163 : : PG_TBLSPC_DIR),
2164 : : errhint("Remove those directories, or set \"allow_in_place_tablespaces\" to ON transiently to let recovery complete.")));
2165 : : }
22 fujii@postgresql.org 2166 : 149 : FreeDir(dir);
1561 alvherre@alvh.no-ip. 2167 : 149 : }
2168 : :
2169 : : /*
2170 : : * Checks if recovery has reached a consistent state. When consistency is
2171 : : * reached and we have a valid starting standby snapshot, tell postmaster
2172 : : * that it can start accepting read-only connections.
2173 : : */
2174 : : static void
1723 heikki.linnakangas@i 2175 : 2941553 : CheckRecoveryConsistency(void)
2176 : : {
2177 : : XLogRecPtr lastReplayedEndRecPtr;
2178 : : TimeLineID lastReplayedTLI;
2179 : :
2180 : : /*
2181 : : * During crash recovery, we don't reach a consistent state until we've
2182 : : * replayed all the WAL.
2183 : : */
364 alvherre@kurilemu.de 2184 [ + + ]: 2941553 : if (!XLogRecPtrIsValid(minRecoveryPoint))
1723 heikki.linnakangas@i 2185 : 322540 : return;
2186 : :
2187 [ - + ]: 2619013 : Assert(InArchiveRecovery);
2188 : :
2189 : : /*
2190 : : * assume that we are called in the startup process, and hence don't need
2191 : : * a lock to read lastReplayedEndRecPtr
2192 : : */
2193 : 2619013 : lastReplayedEndRecPtr = XLogRecoveryCtl->lastReplayedEndRecPtr;
2194 : 2619013 : lastReplayedTLI = XLogRecoveryCtl->lastReplayedTLI;
2195 : :
2196 : : /*
2197 : : * Have we reached the point where our base backup was completed?
2198 : : */
364 alvherre@kurilemu.de 2199 [ + + ]: 2619013 : if (XLogRecPtrIsValid(backupEndPoint) &&
1723 heikki.linnakangas@i 2200 [ + + ]: 5283 : backupEndPoint <= lastReplayedEndRecPtr)
2201 : : {
1015 michael@paquier.xyz 2202 : 102 : XLogRecPtr saveBackupStartPoint = backupStartPoint;
2203 : 102 : XLogRecPtr saveBackupEndPoint = backupEndPoint;
2204 : :
1723 heikki.linnakangas@i 2205 [ + + ]: 102 : elog(DEBUG1, "end of backup reached");
2206 : :
2207 : : /*
2208 : : * We have reached the end of base backup, as indicated by pg_control.
2209 : : * Update the control file accordingly.
2210 : : */
2211 : 102 : ReachedEndOfBackup(lastReplayedEndRecPtr, lastReplayedTLI);
2212 : 102 : backupStartPoint = InvalidXLogRecPtr;
2213 : 102 : backupEndPoint = InvalidXLogRecPtr;
2214 : 102 : backupEndRequired = false;
2215 : :
1015 michael@paquier.xyz 2216 [ + - ]: 102 : ereport(LOG,
2217 : : errmsg("completed backup recovery with redo LSN %X/%08X and end LSN %X/%08X",
2218 : : LSN_FORMAT_ARGS(saveBackupStartPoint),
2219 : : LSN_FORMAT_ARGS(saveBackupEndPoint)));
2220 : : }
2221 : :
2222 : : /*
2223 : : * Have we passed our safe starting point? Note that minRecoveryPoint is
2224 : : * known to be incorrectly set if recovering from a backup, until the
2225 : : * XLOG_BACKUP_END arrives to advise us of the correct minRecoveryPoint.
2226 : : * All we know prior to that is that we're not consistent yet.
2227 : : */
1723 heikki.linnakangas@i 2228 [ + + + + ]: 2619013 : if (!reachedConsistency && !backupEndRequired &&
2229 [ + + ]: 14814 : minRecoveryPoint <= lastReplayedEndRecPtr)
2230 : : {
2231 : : /*
2232 : : * Check to see if the XLOG sequence contained any unresolved
2233 : : * references to uninitialized pages.
2234 : : */
2235 : 149 : XLogCheckInvalidPages();
2236 : :
2237 : : /*
2238 : : * Check that pg_tblspc doesn't contain any real directories. Replay
2239 : : * of Database/CREATE_* records may have created fictitious tablespace
2240 : : * directories that should have been removed by the time consistency
2241 : : * was reached.
2242 : : */
1561 alvherre@alvh.no-ip. 2243 : 149 : CheckTablespaceDirectory();
2244 : :
1723 heikki.linnakangas@i 2245 : 149 : reachedConsistency = true;
582 fujii@postgresql.org 2246 : 149 : SendPostmasterSignal(PMSIGNAL_RECOVERY_CONSISTENT);
1723 heikki.linnakangas@i 2247 [ + - ]: 149 : ereport(LOG,
2248 : : errmsg("consistent recovery state reached at %X/%08X",
2249 : : LSN_FORMAT_ARGS(lastReplayedEndRecPtr)));
2250 : : }
2251 : :
2252 : : /*
2253 : : * Have we got a valid starting snapshot that will allow queries to be
2254 : : * run? If so, we can tell postmaster that the database is consistent now,
2255 : : * enabling connections.
2256 : : */
2257 [ + + ]: 2619013 : if (standbyState == STANDBY_SNAPSHOT_READY &&
2258 [ + + + + ]: 2618713 : !LocalHotStandbyActive &&
2259 [ + - ]: 137 : reachedConsistency &&
2260 : : IsUnderPostmaster)
2261 : : {
2262 : 137 : SpinLockAcquire(&XLogRecoveryCtl->info_lck);
2263 : 137 : XLogRecoveryCtl->SharedHotStandbyActive = true;
2264 : 137 : SpinLockRelease(&XLogRecoveryCtl->info_lck);
2265 : :
2266 : 137 : LocalHotStandbyActive = true;
2267 : :
2268 : 137 : SendPostmasterSignal(PMSIGNAL_BEGIN_HOT_STANDBY);
2269 : : }
2270 : : }
2271 : :
2272 : : /*
2273 : : * Error context callback for errors occurring during rm_redo().
2274 : : */
2275 : : static void
2276 : 188 : rm_redo_error_callback(void *arg)
2277 : : {
2278 : 188 : XLogReaderState *record = (XLogReaderState *) arg;
2279 : : StringInfoData buf;
2280 : :
2281 : 188 : initStringInfo(&buf);
2282 : 188 : xlog_outdesc(&buf, record);
2283 : 188 : xlog_block_info(&buf, record);
2284 : :
2285 : : /* translator: %s is a WAL record description */
486 alvherre@kurilemu.de 2286 : 188 : errcontext("WAL redo at %X/%08X for %s",
1723 heikki.linnakangas@i 2287 : 188 : LSN_FORMAT_ARGS(record->ReadRecPtr),
2288 : : buf.data);
2289 : :
2290 : 188 : pfree(buf.data);
2291 : 188 : }
2292 : :
2293 : : /*
2294 : : * Returns a string describing an XLogRecord, consisting of its identity
2295 : : * optionally followed by a colon, a space, and a further description.
2296 : : */
2297 : : void
2298 : 188 : xlog_outdesc(StringInfo buf, XLogReaderState *record)
2299 : : {
1674 jdavis@postgresql.or 2300 : 188 : RmgrData rmgr = GetRmgr(XLogRecGetRmid(record));
1723 heikki.linnakangas@i 2301 : 188 : uint8 info = XLogRecGetInfo(record);
2302 : : const char *id;
2303 : :
1674 jdavis@postgresql.or 2304 : 188 : appendStringInfoString(buf, rmgr.rm_name);
1723 heikki.linnakangas@i 2305 : 188 : appendStringInfoChar(buf, '/');
2306 : :
1674 jdavis@postgresql.or 2307 : 188 : id = rmgr.rm_identify(info);
1723 heikki.linnakangas@i 2308 [ - + ]: 188 : if (id == NULL)
1723 heikki.linnakangas@i 2309 :UBC 0 : appendStringInfo(buf, "UNKNOWN (%X): ", info & ~XLR_INFO_MASK);
2310 : : else
1723 heikki.linnakangas@i 2311 :CBC 188 : appendStringInfo(buf, "%s: ", id);
2312 : :
1674 jdavis@postgresql.or 2313 : 188 : rmgr.rm_desc(buf, record);
1723 heikki.linnakangas@i 2314 : 188 : }
2315 : :
2316 : : #ifdef WAL_DEBUG
2317 : :
2318 : : static void
2319 : : xlog_outrec(StringInfo buf, XLogReaderState *record)
2320 : : {
2321 : : appendStringInfo(buf, "prev %X/%08X; xid %u",
2322 : : LSN_FORMAT_ARGS(XLogRecGetPrev(record)),
2323 : : XLogRecGetXid(record));
2324 : :
2325 : : appendStringInfo(buf, "; len %u",
2326 : : XLogRecGetDataLen(record));
2327 : :
2328 : : xlog_block_info(buf, record);
2329 : : }
2330 : : #endif /* WAL_DEBUG */
2331 : :
2332 : : /*
2333 : : * Returns a string giving information about all the blocks in an
2334 : : * XLogRecord.
2335 : : */
2336 : : static void
2337 : 188 : xlog_block_info(StringInfo buf, XLogReaderState *record)
2338 : : {
2339 : : int block_id;
2340 : :
2341 : : /* decode block references */
1693 tmunro@postgresql.or 2342 [ + + ]: 238 : for (block_id = 0; block_id <= XLogRecMaxBlockId(record); block_id++)
2343 : : {
2344 : : RelFileLocator rlocator;
2345 : : ForkNumber forknum;
2346 : : BlockNumber blk;
2347 : :
1669 tgl@sss.pgh.pa.us 2348 [ - + ]: 50 : if (!XLogRecGetBlockTagExtended(record, block_id,
2349 : : &rlocator, &forknum, &blk, NULL))
1723 heikki.linnakangas@i 2350 :UBC 0 : continue;
2351 : :
1723 heikki.linnakangas@i 2352 [ + + ]:CBC 50 : if (forknum != MAIN_FORKNUM)
1499 rhaas@postgresql.org 2353 : 7 : appendStringInfo(buf, "; blkref #%d: rel %u/%u/%u, fork %u, blk %u",
2354 : : block_id,
2355 : : rlocator.spcOid, rlocator.dbOid,
2356 : : rlocator.relNumber,
2357 : : forknum,
2358 : : blk);
2359 : : else
2360 : 43 : appendStringInfo(buf, "; blkref #%d: rel %u/%u/%u, blk %u",
2361 : : block_id,
2362 : : rlocator.spcOid, rlocator.dbOid,
2363 : : rlocator.relNumber,
2364 : : blk);
1723 heikki.linnakangas@i 2365 [ + + ]: 50 : if (XLogRecHasBlockImage(record, block_id))
2366 : 30 : appendStringInfoString(buf, " FPW");
2367 : : }
2368 : 188 : }
2369 : :
2370 : :
2371 : : /*
2372 : : * Check that it's OK to switch to new timeline during recovery.
2373 : : *
2374 : : * 'lsn' is the address of the shutdown checkpoint record we're about to
2375 : : * replay. (Currently, timeline can only change at a shutdown checkpoint).
2376 : : */
2377 : : static void
2378 : 15 : checkTimeLineSwitch(XLogRecPtr lsn, TimeLineID newTLI, TimeLineID prevTLI,
2379 : : TimeLineID replayTLI)
2380 : : {
2381 : : /* Check that the record agrees on what the current (old) timeline is */
2382 [ - + ]: 15 : if (prevTLI != replayTLI)
1723 heikki.linnakangas@i 2383 [ # # ]:UBC 0 : ereport(PANIC,
2384 : : (errmsg("unexpected previous timeline ID %u (current timeline ID %u) in checkpoint record",
2385 : : prevTLI, replayTLI)));
2386 : :
2387 : : /*
2388 : : * The new timeline better be in the list of timelines we expect to see,
2389 : : * according to the timeline history. It should also not decrease.
2390 : : */
1723 heikki.linnakangas@i 2391 [ + - - + ]:CBC 15 : if (newTLI < replayTLI || !tliInHistory(newTLI, expectedTLEs))
1723 heikki.linnakangas@i 2392 [ # # ]:UBC 0 : ereport(PANIC,
2393 : : (errmsg("unexpected timeline ID %u (after %u) in checkpoint record",
2394 : : newTLI, replayTLI)));
2395 : :
2396 : : /*
2397 : : * If we have not yet reached min recovery point, and we're about to
2398 : : * switch to a timeline greater than the timeline of the min recovery
2399 : : * point: trouble. After switching to the new timeline, we could not
2400 : : * possibly visit the min recovery point on the correct timeline anymore.
2401 : : * This can happen if there is a newer timeline in the archive that
2402 : : * branched before the timeline the min recovery point is on, and you
2403 : : * attempt to do PITR to the new timeline.
2404 : : */
364 alvherre@kurilemu.de 2405 [ + + ]:CBC 15 : if (XLogRecPtrIsValid(minRecoveryPoint) &&
1723 heikki.linnakangas@i 2406 [ + + ]: 14 : lsn < minRecoveryPoint &&
2407 [ - + ]: 3 : newTLI > minRecoveryPointTLI)
1723 heikki.linnakangas@i 2408 [ # # ]:UBC 0 : ereport(PANIC,
2409 : : errmsg("unexpected timeline ID %u in checkpoint record, before reaching minimum recovery point %X/%08X on timeline %u",
2410 : : newTLI,
2411 : : LSN_FORMAT_ARGS(minRecoveryPoint),
2412 : : minRecoveryPointTLI));
2413 : :
2414 : : /* Looks good */
1723 heikki.linnakangas@i 2415 :CBC 15 : }
2416 : :
2417 : :
2418 : : /*
2419 : : * Extract timestamp from WAL record.
2420 : : *
2421 : : * If the record contains a timestamp, returns true, and saves the timestamp
2422 : : * in *recordXtime. If the record type has no timestamp, returns false.
2423 : : * Currently, only transaction commit/abort records and restore points contain
2424 : : * timestamps.
2425 : : */
2426 : : static bool
2427 : 46914 : getRecordTimestamp(XLogReaderState *record, TimestampTz *recordXtime)
2428 : : {
2429 : 46914 : uint8 info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
2430 : 46914 : uint8 xact_info = info & XLOG_XACT_OPMASK;
2431 : 46914 : uint8 rmid = XLogRecGetRmid(record);
2432 : :
2433 [ + + + - ]: 46914 : if (rmid == RM_XLOG_ID && info == XLOG_RESTORE_POINT)
2434 : : {
2435 : 2 : *recordXtime = ((xl_restore_point *) XLogRecGetData(record))->rp_time;
2436 : 2 : return true;
2437 : : }
2438 [ + - + + : 46912 : if (rmid == RM_XACT_ID && (xact_info == XLOG_XACT_COMMIT ||
+ + ]
2439 : : xact_info == XLOG_XACT_COMMIT_PREPARED))
2440 : : {
2441 : 42908 : *recordXtime = ((xl_xact_commit *) XLogRecGetData(record))->xact_time;
2442 : 42908 : return true;
2443 : : }
2444 [ + - + + : 4004 : if (rmid == RM_XACT_ID && (xact_info == XLOG_XACT_ABORT ||
+ - ]
2445 : : xact_info == XLOG_XACT_ABORT_PREPARED))
2446 : : {
2447 : 4004 : *recordXtime = ((xl_xact_abort *) XLogRecGetData(record))->xact_time;
2448 : 4004 : return true;
2449 : : }
1723 heikki.linnakangas@i 2450 :UBC 0 : return false;
2451 : : }
2452 : :
2453 : : /*
2454 : : * Checks whether the current buffer page and backup page stored in the
2455 : : * WAL record are consistent or not. Before comparing the two pages, a
2456 : : * masking can be applied to the pages to ignore certain areas like hint bits,
2457 : : * unused space between pd_lower and pd_upper among other things. This
2458 : : * function should be called once WAL replay has been completed for a
2459 : : * given record.
2460 : : */
2461 : : static void
1723 heikki.linnakangas@i 2462 :CBC 2246999 : verifyBackupPageConsistency(XLogReaderState *record)
2463 : : {
1674 jdavis@postgresql.or 2464 : 2246999 : RmgrData rmgr = GetRmgr(XLogRecGetRmid(record));
2465 : : RelFileLocator rlocator;
2466 : : ForkNumber forknum;
2467 : : BlockNumber blkno;
2468 : : int block_id;
2469 : :
2470 : : /* Records with no backup blocks have no need for consistency checks. */
1723 heikki.linnakangas@i 2471 [ + + ]: 2246999 : if (!XLogRecHasAnyBlockRefs(record))
2472 : 93 : return;
2473 : :
2474 [ - + ]: 2246906 : Assert((XLogRecGetInfo(record) & XLR_CHECK_CONSISTENCY) != 0);
2475 : :
1693 tmunro@postgresql.or 2476 [ + + ]: 4670673 : for (block_id = 0; block_id <= XLogRecMaxBlockId(record); block_id++)
2477 : : {
2478 : : Buffer buf;
2479 : : Page page;
2480 : :
1669 tgl@sss.pgh.pa.us 2481 [ + + ]: 2423767 : if (!XLogRecGetBlockTagExtended(record, block_id,
2482 : : &rlocator, &forknum, &blkno, NULL))
2483 : : {
2484 : : /*
2485 : : * WAL record doesn't contain a block reference with the given id.
2486 : : * Do nothing.
2487 : : */
1723 heikki.linnakangas@i 2488 : 2441 : continue;
2489 : : }
2490 : :
2491 : : /*
2492 : : * Blocks registered with REGBUF_NO_CHANGE may have no image, see
2493 : : * XLogRecordAssemble(). Nothing to compare for these.
2494 : : */
7 michael@paquier.xyz 2495 [ + + ]: 2421326 : if (!XLogRecHasBlockImage(record, block_id))
2496 : 51 : continue;
2497 : :
1723 heikki.linnakangas@i 2498 [ + + ]: 2421275 : if (XLogRecBlockImageApply(record, block_id))
2499 : : {
2500 : : /*
2501 : : * WAL record has already applied the page, so bypass the
2502 : : * consistency check as that would result in comparing the full
2503 : : * page stored in the record with itself.
2504 : : */
2505 : 28777 : continue;
2506 : : }
2507 : :
2508 : : /*
2509 : : * Read the contents from the current buffer and store it in a
2510 : : * temporary page.
2511 : : */
1583 rhaas@postgresql.org 2512 : 2392498 : buf = XLogReadBufferExtended(rlocator, forknum, blkno,
2513 : : RBM_NORMAL_NO_LOG,
2514 : : InvalidBuffer);
1723 heikki.linnakangas@i 2515 [ - + ]: 2392498 : if (!BufferIsValid(buf))
1723 heikki.linnakangas@i 2516 :UBC 0 : continue;
2517 : :
1723 heikki.linnakangas@i 2518 :CBC 2392498 : LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
2519 : 2392498 : page = BufferGetPage(buf);
2520 : :
2521 : : /*
2522 : : * Take a copy of the local page where WAL has been applied to have a
2523 : : * comparison base before masking it...
2524 : : */
2525 : 2392498 : memcpy(replay_image_masked, page, BLCKSZ);
2526 : :
2527 : : /* No need for this page anymore now that a copy is in. */
2528 : 2392498 : UnlockReleaseBuffer(buf);
2529 : :
2530 : : /*
2531 : : * If the block LSN is already ahead of this WAL record, we can't
2532 : : * expect contents to match. This can happen if recovery is
2533 : : * restarted.
2534 : : */
2535 [ - + ]: 2392498 : if (PageGetLSN(replay_image_masked) > record->EndRecPtr)
1723 heikki.linnakangas@i 2536 :UBC 0 : continue;
2537 : :
2538 : : /*
2539 : : * Read the contents from the backup copy, stored in WAL record and
2540 : : * store it in a temporary page. There is no need to allocate a new
2541 : : * page here, a local buffer is fine to hold its contents and a mask
2542 : : * can be directly applied on it.
2543 : : */
1723 heikki.linnakangas@i 2544 [ - + ]:CBC 2392498 : if (!RestoreBlockImage(record, block_id, primary_image_masked))
1518 michael@paquier.xyz 2545 [ # # ]:UBC 0 : ereport(ERROR,
2546 : : (errcode(ERRCODE_INTERNAL_ERROR),
2547 : : errmsg_internal("%s", record->errormsg_buf)));
2548 : :
2549 : : /*
2550 : : * If masking function is defined, mask both the primary and replay
2551 : : * images
2552 : : */
1674 jdavis@postgresql.or 2553 [ + - ]:CBC 2392498 : if (rmgr.rm_mask != NULL)
2554 : : {
2555 : 2392498 : rmgr.rm_mask(replay_image_masked, blkno);
2556 : 2392498 : rmgr.rm_mask(primary_image_masked, blkno);
2557 : : }
2558 : :
2559 : : /* Time to compare the primary and replay images. */
1723 heikki.linnakangas@i 2560 [ - + ]: 2392498 : if (memcmp(replay_image_masked, primary_image_masked, BLCKSZ) != 0)
2561 : : {
1723 heikki.linnakangas@i 2562 [ # # ]:UBC 0 : elog(FATAL,
2563 : : "inconsistent page found, rel %u/%u/%u, forknum %u, blkno %u",
2564 : : rlocator.spcOid, rlocator.dbOid, rlocator.relNumber,
2565 : : forknum, blkno);
2566 : : }
2567 : : }
2568 : : }
2569 : :
2570 : : /*
2571 : : * For point-in-time recovery, this function decides whether we want to
2572 : : * stop applying the XLOG before the current record.
2573 : : *
2574 : : * Returns true if we are stopping, false otherwise. If stopping, some
2575 : : * information is saved in recoveryStopXid et al for use in annotating the
2576 : : * new timeline's history file.
2577 : : */
2578 : : static bool
1723 heikki.linnakangas@i 2579 :CBC 2941302 : recoveryStopsBefore(XLogReaderState *record)
2580 : : {
2581 : 2941302 : bool stopsHere = false;
2582 : : uint8 xact_info;
2583 : : bool isCommit;
2584 : 2941302 : TimestampTz recordXtime = 0;
2585 : : TransactionId recordXid;
2586 : :
2587 : : /*
2588 : : * Ignore recovery target settings when not in archive recovery (meaning
2589 : : * we are in crash recovery).
2590 : : */
2591 [ + + ]: 2941302 : if (!ArchiveRecoveryRequested)
2592 : 307796 : return false;
2593 : :
2594 : : /* Check if we should stop as soon as reaching consistency */
2595 [ - + - - ]: 2633506 : if (recoveryTarget == RECOVERY_TARGET_IMMEDIATE && reachedConsistency)
2596 : : {
1723 heikki.linnakangas@i 2597 [ # # ]:UBC 0 : ereport(LOG,
2598 : : (errmsg("recovery stopping after reaching consistency")));
2599 : :
2600 : 0 : recoveryStopAfter = false;
2601 : 0 : recoveryStopXid = InvalidTransactionId;
2602 : 0 : recoveryStopLSN = InvalidXLogRecPtr;
2603 : 0 : recoveryStopTime = 0;
2604 : 0 : recoveryStopName[0] = '\0';
2605 : 0 : return true;
2606 : : }
2607 : :
2608 : : /* Check if target LSN has been reached */
1723 heikki.linnakangas@i 2609 [ + + ]:CBC 2633506 : if (recoveryTarget == RECOVERY_TARGET_LSN &&
2610 [ + + ]: 8511 : !recoveryTargetInclusive &&
2611 [ + + ]: 489 : record->ReadRecPtr >= recoveryTargetLSN)
2612 : : {
2613 : 2 : recoveryStopAfter = false;
2614 : 2 : recoveryStopXid = InvalidTransactionId;
2615 : 2 : recoveryStopLSN = record->ReadRecPtr;
2616 : 2 : recoveryStopTime = 0;
2617 : 2 : recoveryStopName[0] = '\0';
2618 [ + - ]: 2 : ereport(LOG,
2619 : : errmsg("recovery stopping before WAL location (LSN) \"%X/%08X\"",
2620 : : LSN_FORMAT_ARGS(recoveryStopLSN)));
2621 : 2 : return true;
2622 : : }
2623 : :
2624 : : /* Otherwise we only consider stopping before COMMIT or ABORT records. */
2625 [ + + ]: 2633504 : if (XLogRecGetRmid(record) != RM_XACT_ID)
2626 : 2609731 : return false;
2627 : :
2628 : 23773 : xact_info = XLogRecGetInfo(record) & XLOG_XACT_OPMASK;
2629 : :
2630 [ + + ]: 23773 : if (xact_info == XLOG_XACT_COMMIT)
2631 : : {
2632 : 21414 : isCommit = true;
2633 : 21414 : recordXid = XLogRecGetXid(record);
2634 : : }
2635 [ + + ]: 2359 : else if (xact_info == XLOG_XACT_COMMIT_PREPARED)
2636 : : {
2637 : 26 : xl_xact_commit *xlrec = (xl_xact_commit *) XLogRecGetData(record);
2638 : : xl_xact_parsed_commit parsed;
2639 : :
2640 : 26 : isCommit = true;
2641 : 26 : ParseCommitRecord(XLogRecGetInfo(record),
2642 : : xlrec,
2643 : : &parsed);
2644 : 26 : recordXid = parsed.twophase_xid;
2645 : : }
2646 [ + + ]: 2333 : else if (xact_info == XLOG_XACT_ABORT)
2647 : : {
2648 : 1987 : isCommit = false;
2649 : 1987 : recordXid = XLogRecGetXid(record);
2650 : : }
2651 [ + + ]: 346 : else if (xact_info == XLOG_XACT_ABORT_PREPARED)
2652 : : {
2653 : 15 : xl_xact_abort *xlrec = (xl_xact_abort *) XLogRecGetData(record);
2654 : : xl_xact_parsed_abort parsed;
2655 : :
2656 : 15 : isCommit = false;
2657 : 15 : ParseAbortRecord(XLogRecGetInfo(record),
2658 : : xlrec,
2659 : : &parsed);
2660 : 15 : recordXid = parsed.twophase_xid;
2661 : : }
2662 : : else
2663 : 331 : return false;
2664 : :
2665 [ - + - - ]: 23442 : if (recoveryTarget == RECOVERY_TARGET_XID && !recoveryTargetInclusive)
2666 : : {
2667 : : /*
2668 : : * There can be only one transaction end record with this exact
2669 : : * transactionid
2670 : : *
2671 : : * when testing for an xid, we MUST test for equality only, since
2672 : : * transactions are numbered in the order they start, not the order
2673 : : * they complete. A higher numbered xid will complete before you about
2674 : : * 50% of the time...
2675 : : */
1723 heikki.linnakangas@i 2676 :UBC 0 : stopsHere = (recordXid == recoveryTargetXid);
2677 : : }
2678 : :
2679 : : /*
2680 : : * Note: we must fetch recordXtime regardless of recoveryTarget setting.
2681 : : * We don't expect getRecordTimestamp ever to fail, since we already know
2682 : : * this is a commit or abort record; but test its result anyway.
2683 : : */
1386 tgl@sss.pgh.pa.us 2684 [ + - ]:CBC 23442 : if (getRecordTimestamp(record, &recordXtime) &&
2685 [ - + ]: 23442 : recoveryTarget == RECOVERY_TARGET_TIME)
2686 : : {
2687 : : /*
2688 : : * There can be many transactions that share the same commit time, so
2689 : : * we stop after the last one, if we are inclusive, or stop at the
2690 : : * first one if we are exclusive
2691 : : */
1723 heikki.linnakangas@i 2692 [ # # ]:UBC 0 : if (recoveryTargetInclusive)
2693 : 0 : stopsHere = (recordXtime > recoveryTargetTime);
2694 : : else
2695 : 0 : stopsHere = (recordXtime >= recoveryTargetTime);
2696 : : }
2697 : :
1723 heikki.linnakangas@i 2698 [ - + ]:CBC 23442 : if (stopsHere)
2699 : : {
1723 heikki.linnakangas@i 2700 :UBC 0 : recoveryStopAfter = false;
2701 : 0 : recoveryStopXid = recordXid;
2702 : 0 : recoveryStopTime = recordXtime;
2703 : 0 : recoveryStopLSN = InvalidXLogRecPtr;
2704 : 0 : recoveryStopName[0] = '\0';
2705 : :
2706 [ # # ]: 0 : if (isCommit)
2707 : : {
2708 [ # # ]: 0 : ereport(LOG,
2709 : : (errmsg("recovery stopping before commit of transaction %u, time %s",
2710 : : recoveryStopXid,
2711 : : timestamptz_to_str(recoveryStopTime))));
2712 : : }
2713 : : else
2714 : : {
2715 [ # # ]: 0 : ereport(LOG,
2716 : : (errmsg("recovery stopping before abort of transaction %u, time %s",
2717 : : recoveryStopXid,
2718 : : timestamptz_to_str(recoveryStopTime))));
2719 : : }
2720 : : }
2721 : :
1723 heikki.linnakangas@i 2722 :CBC 23442 : return stopsHere;
2723 : : }
2724 : :
2725 : : /*
2726 : : * Same as recoveryStopsBefore, but called after applying the record.
2727 : : *
2728 : : * We also track the timestamp of the latest applied COMMIT/ABORT
2729 : : * record in XLogRecoveryCtl->recoveryLastXTime.
2730 : : */
2731 : : static bool
2732 : 2941298 : recoveryStopsAfter(XLogReaderState *record)
2733 : : {
2734 : : uint8 info;
2735 : : uint8 xact_info;
2736 : : uint8 rmid;
1248 2737 : 2941298 : TimestampTz recordXtime = 0;
2738 : :
2739 : : /*
2740 : : * Ignore recovery target settings when not in archive recovery (meaning
2741 : : * we are in crash recovery).
2742 : : */
1723 2743 [ + + ]: 2941298 : if (!ArchiveRecoveryRequested)
2744 : 307796 : return false;
2745 : :
2746 : 2633502 : info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
2747 : 2633502 : rmid = XLogRecGetRmid(record);
2748 : :
2749 : : /*
2750 : : * There can be many restore points that share the same name; we stop at
2751 : : * the first one.
2752 : : */
2753 [ + + + + ]: 2633502 : if (recoveryTarget == RECOVERY_TARGET_NAME &&
2754 [ + + ]: 20 : rmid == RM_XLOG_ID && info == XLOG_RESTORE_POINT)
2755 : : {
2756 : : xl_restore_point *recordRestorePointData;
2757 : :
2758 : 3 : recordRestorePointData = (xl_restore_point *) XLogRecGetData(record);
2759 : :
2760 [ + + ]: 3 : if (strcmp(recordRestorePointData->rp_name, recoveryTargetName) == 0)
2761 : : {
2762 : 2 : recoveryStopAfter = true;
2763 : 2 : recoveryStopXid = InvalidTransactionId;
2764 : 2 : recoveryStopLSN = InvalidXLogRecPtr;
2765 : 2 : (void) getRecordTimestamp(record, &recoveryStopTime);
2766 : 2 : strlcpy(recoveryStopName, recordRestorePointData->rp_name, MAXFNAMELEN);
2767 : :
2768 [ + - ]: 2 : ereport(LOG,
2769 : : (errmsg("recovery stopping at restore point \"%s\", time %s",
2770 : : recoveryStopName,
2771 : : timestamptz_to_str(recoveryStopTime))));
2772 : 2 : return true;
2773 : : }
2774 : : }
2775 : :
2776 : : /* Check if the target LSN has been reached */
2777 [ + + + + ]: 2633500 : if (recoveryTarget == RECOVERY_TARGET_LSN &&
2778 : 8022 : recoveryTargetInclusive &&
2779 [ + + ]: 8022 : record->ReadRecPtr >= recoveryTargetLSN)
2780 : : {
2781 : 3 : recoveryStopAfter = true;
2782 : 3 : recoveryStopXid = InvalidTransactionId;
2783 : 3 : recoveryStopLSN = record->ReadRecPtr;
2784 : 3 : recoveryStopTime = 0;
2785 : 3 : recoveryStopName[0] = '\0';
2786 [ + - ]: 3 : ereport(LOG,
2787 : : errmsg("recovery stopping after WAL location (LSN) \"%X/%08X\"",
2788 : : LSN_FORMAT_ARGS(recoveryStopLSN)));
2789 : 3 : return true;
2790 : : }
2791 : :
2792 [ + + ]: 2633497 : if (rmid != RM_XACT_ID)
2793 : 2609726 : return false;
2794 : :
2795 : 23771 : xact_info = info & XLOG_XACT_OPMASK;
2796 : :
2797 [ + + + + ]: 23771 : if (xact_info == XLOG_XACT_COMMIT ||
2798 [ + + ]: 2333 : xact_info == XLOG_XACT_COMMIT_PREPARED ||
2799 [ + + ]: 346 : xact_info == XLOG_XACT_ABORT ||
2800 : : xact_info == XLOG_XACT_ABORT_PREPARED)
2801 : : {
2802 : : TransactionId recordXid;
2803 : :
2804 : : /* Update the last applied transaction timestamp */
2805 [ + - ]: 23440 : if (getRecordTimestamp(record, &recordXtime))
2806 : 23440 : SetLatestXTime(recordXtime);
2807 : :
2808 : : /* Extract the XID of the committed/aborted transaction */
2809 [ + + ]: 23440 : if (xact_info == XLOG_XACT_COMMIT_PREPARED)
2810 : : {
2811 : 26 : xl_xact_commit *xlrec = (xl_xact_commit *) XLogRecGetData(record);
2812 : : xl_xact_parsed_commit parsed;
2813 : :
2814 : 26 : ParseCommitRecord(XLogRecGetInfo(record),
2815 : : xlrec,
2816 : : &parsed);
2817 : 26 : recordXid = parsed.twophase_xid;
2818 : : }
2819 [ + + ]: 23414 : else if (xact_info == XLOG_XACT_ABORT_PREPARED)
2820 : : {
2821 : 15 : xl_xact_abort *xlrec = (xl_xact_abort *) XLogRecGetData(record);
2822 : : xl_xact_parsed_abort parsed;
2823 : :
2824 : 15 : ParseAbortRecord(XLogRecGetInfo(record),
2825 : : xlrec,
2826 : : &parsed);
2827 : 15 : recordXid = parsed.twophase_xid;
2828 : : }
2829 : : else
2830 : 23399 : recordXid = XLogRecGetXid(record);
2831 : :
2832 : : /*
2833 : : * There can be only one transaction end record with this exact
2834 : : * transactionid
2835 : : *
2836 : : * when testing for an xid, we MUST test for equality only, since
2837 : : * transactions are numbered in the order they start, not the order
2838 : : * they complete. A higher numbered xid will complete before you about
2839 : : * 50% of the time...
2840 : : */
2841 [ - + - - ]: 23440 : if (recoveryTarget == RECOVERY_TARGET_XID && recoveryTargetInclusive &&
1723 heikki.linnakangas@i 2842 [ # # ]:UBC 0 : recordXid == recoveryTargetXid)
2843 : : {
2844 : 0 : recoveryStopAfter = true;
2845 : 0 : recoveryStopXid = recordXid;
2846 : 0 : recoveryStopTime = recordXtime;
2847 : 0 : recoveryStopLSN = InvalidXLogRecPtr;
2848 : 0 : recoveryStopName[0] = '\0';
2849 : :
2850 [ # # # # ]: 0 : if (xact_info == XLOG_XACT_COMMIT ||
2851 : : xact_info == XLOG_XACT_COMMIT_PREPARED)
2852 : : {
2853 [ # # ]: 0 : ereport(LOG,
2854 : : (errmsg("recovery stopping after commit of transaction %u, time %s",
2855 : : recoveryStopXid,
2856 : : timestamptz_to_str(recoveryStopTime))));
2857 : : }
2858 [ # # # # ]: 0 : else if (xact_info == XLOG_XACT_ABORT ||
2859 : : xact_info == XLOG_XACT_ABORT_PREPARED)
2860 : : {
2861 [ # # ]: 0 : ereport(LOG,
2862 : : (errmsg("recovery stopping after abort of transaction %u, time %s",
2863 : : recoveryStopXid,
2864 : : timestamptz_to_str(recoveryStopTime))));
2865 : : }
2866 : 0 : return true;
2867 : : }
2868 : : }
2869 : :
2870 : : /* Check if we should stop as soon as reaching consistency */
1723 heikki.linnakangas@i 2871 [ - + - - ]:CBC 23771 : if (recoveryTarget == RECOVERY_TARGET_IMMEDIATE && reachedConsistency)
2872 : : {
1723 heikki.linnakangas@i 2873 [ # # ]:UBC 0 : ereport(LOG,
2874 : : (errmsg("recovery stopping after reaching consistency")));
2875 : :
2876 : 0 : recoveryStopAfter = true;
2877 : 0 : recoveryStopXid = InvalidTransactionId;
2878 : 0 : recoveryStopTime = 0;
2879 : 0 : recoveryStopLSN = InvalidXLogRecPtr;
2880 : 0 : recoveryStopName[0] = '\0';
2881 : 0 : return true;
2882 : : }
2883 : :
1723 heikki.linnakangas@i 2884 :CBC 23771 : return false;
2885 : : }
2886 : :
2887 : : /*
2888 : : * Create a comment for the history file to explain why and where
2889 : : * timeline changed.
2890 : : */
2891 : : static char *
2892 : 1052 : getRecoveryStopReason(void)
2893 : : {
2894 : : char reason[200];
2895 : :
2896 [ - + ]: 1052 : if (recoveryTarget == RECOVERY_TARGET_XID)
1723 heikki.linnakangas@i 2897 :UBC 0 : snprintf(reason, sizeof(reason),
2898 : : "%s transaction %u",
2899 [ # # ]: 0 : recoveryStopAfter ? "after" : "before",
2900 : : recoveryStopXid);
1723 heikki.linnakangas@i 2901 [ - + ]:CBC 1052 : else if (recoveryTarget == RECOVERY_TARGET_TIME)
1723 heikki.linnakangas@i 2902 :UBC 0 : snprintf(reason, sizeof(reason),
2903 : : "%s %s\n",
2904 [ # # ]: 0 : recoveryStopAfter ? "after" : "before",
2905 : : timestamptz_to_str(recoveryStopTime));
1723 heikki.linnakangas@i 2906 [ + + ]:CBC 1052 : else if (recoveryTarget == RECOVERY_TARGET_LSN)
2907 : 6 : snprintf(reason, sizeof(reason),
2908 : : "%s LSN %X/%08X\n",
2909 : 6 : recoveryStopAfter ? "after" : "before",
2910 [ + + ]: 6 : LSN_FORMAT_ARGS(recoveryStopLSN));
2911 [ + + ]: 1046 : else if (recoveryTarget == RECOVERY_TARGET_NAME)
2912 : 3 : snprintf(reason, sizeof(reason),
2913 : : "at restore point \"%s\"",
2914 : : recoveryStopName);
2915 [ - + ]: 1043 : else if (recoveryTarget == RECOVERY_TARGET_IMMEDIATE)
1723 heikki.linnakangas@i 2916 :UBC 0 : snprintf(reason, sizeof(reason), "reached consistency");
2917 : : else
1723 heikki.linnakangas@i 2918 :CBC 1043 : snprintf(reason, sizeof(reason), "no recovery target specified");
2919 : :
2920 : 1052 : return pstrdup(reason);
2921 : : }
2922 : :
2923 : : /*
2924 : : * Wait until shared recoveryPauseState is set to RECOVERY_NOT_PAUSED.
2925 : : *
2926 : : * endOfRecovery is true if the recovery target is reached and
2927 : : * the paused state starts at the end of recovery because of
2928 : : * recovery_target_action=pause, and false otherwise.
2929 : : */
2930 : : static void
2931 : 9 : recoveryPausesHere(bool endOfRecovery)
2932 : : {
2933 : : /* Don't pause unless users can connect! */
2934 [ - + ]: 9 : if (!LocalHotStandbyActive)
1723 heikki.linnakangas@i 2935 :UBC 0 : return;
2936 : :
2937 : : /* Don't pause after standby promotion has been triggered */
1723 heikki.linnakangas@i 2938 [ - + ]:CBC 9 : if (LocalPromoteIsTriggered)
1723 heikki.linnakangas@i 2939 :UBC 0 : return;
2940 : :
1723 heikki.linnakangas@i 2941 [ + + ]:CBC 9 : if (endOfRecovery)
2942 [ + - ]: 1 : ereport(LOG,
2943 : : (errmsg("pausing at the end of recovery"),
2944 : : errhint("Execute pg_wal_replay_resume() to promote.")));
2945 : : else
2946 [ + - ]: 8 : ereport(LOG,
2947 : : (errmsg("recovery has paused"),
2948 : : errhint("Execute pg_wal_replay_resume() to continue.")));
2949 : :
2950 : : /* loop until recoveryPauseState is set to RECOVERY_NOT_PAUSED */
2951 [ + + ]: 28 : while (GetRecoveryPauseState() != RECOVERY_NOT_PAUSED)
2952 : : {
610 2953 : 21 : ProcessStartupProcInterrupts();
1723 2954 [ + + ]: 21 : if (CheckForStandbyTrigger())
2955 : 2 : return;
2956 : :
2957 : : /*
2958 : : * If recovery pause is requested then set it paused. While we are in
2959 : : * the loop, user might resume and pause again so set this every time.
2960 : : */
2961 : 19 : ConfirmRecoveryPaused();
2962 : :
2963 : : /*
2964 : : * We wait on a condition variable that will wake us as soon as the
2965 : : * pause ends, but we use a timeout so we can check the above exit
2966 : : * condition periodically too.
2967 : : */
2968 : 19 : ConditionVariableTimedSleep(&XLogRecoveryCtl->recoveryNotPausedCV, 1000,
2969 : : WAIT_EVENT_RECOVERY_PAUSE);
2970 : : }
2971 : 7 : ConditionVariableCancelSleep();
2972 : : }
2973 : :
2974 : : /*
2975 : : * When recovery_min_apply_delay is set, we wait long enough to make sure
2976 : : * certain record types are applied at least that interval behind the primary.
2977 : : *
2978 : : * Returns true if we waited.
2979 : : *
2980 : : * Note that the delay is calculated between the WAL record log time and
2981 : : * the current time on standby. We would prefer to keep track of when this
2982 : : * standby received each WAL record, which would allow a more consistent
2983 : : * approach and one not affected by time synchronisation issues, but that
2984 : : * is significantly more effort and complexity for little actual gain in
2985 : : * usability.
2986 : : */
2987 : : static bool
2988 : 2941300 : recoveryApplyDelay(XLogReaderState *record)
2989 : : {
2990 : : uint8 xact_info;
2991 : : TimestampTz xtime;
2992 : : TimestampTz delayUntil;
2993 : : long msecs;
2994 : :
2995 : : /* nothing to do if no delay configured */
2996 [ + + ]: 2941300 : if (recovery_min_apply_delay <= 0)
2997 : 2941146 : return false;
2998 : :
2999 : : /* no delay is applied on a database not yet consistent */
3000 [ + + ]: 154 : if (!reachedConsistency)
3001 : 4 : return false;
3002 : :
3003 : : /* nothing to do if crash recovery is requested */
3004 [ - + ]: 150 : if (!ArchiveRecoveryRequested)
1723 heikki.linnakangas@i 3005 :UBC 0 : return false;
3006 : :
3007 : : /*
3008 : : * Is it a COMMIT record?
3009 : : *
3010 : : * We deliberately choose not to delay aborts since they have no effect on
3011 : : * MVCC. We already allow replay of records that don't have a timestamp,
3012 : : * so there is already opportunity for issues caused by early conflicts on
3013 : : * standbys.
3014 : : */
1723 heikki.linnakangas@i 3015 [ + + ]:CBC 150 : if (XLogRecGetRmid(record) != RM_XACT_ID)
3016 : 120 : return false;
3017 : :
3018 : 30 : xact_info = XLogRecGetInfo(record) & XLOG_XACT_OPMASK;
3019 : :
3020 [ - + - - ]: 30 : if (xact_info != XLOG_XACT_COMMIT &&
3021 : : xact_info != XLOG_XACT_COMMIT_PREPARED)
1723 heikki.linnakangas@i 3022 :UBC 0 : return false;
3023 : :
1723 heikki.linnakangas@i 3024 [ - + ]:CBC 30 : if (!getRecordTimestamp(record, &xtime))
1723 heikki.linnakangas@i 3025 :UBC 0 : return false;
3026 : :
1723 heikki.linnakangas@i 3027 :CBC 30 : delayUntil = TimestampTzPlusMilliseconds(xtime, recovery_min_apply_delay);
3028 : :
3029 : : /*
3030 : : * Exit without arming the latch if it's already past time to apply this
3031 : : * record
3032 : : */
3033 : 30 : msecs = TimestampDifferenceMilliseconds(GetCurrentTimestamp(), delayUntil);
3034 [ + + ]: 30 : if (msecs <= 0)
3035 : 5 : return false;
3036 : :
3037 : : while (true)
3038 : : {
3039 : 64 : ResetLatch(&XLogRecoveryCtl->recoveryWakeupLatch);
3040 : :
3041 : : /* This might change recovery_min_apply_delay. */
610 3042 : 64 : ProcessStartupProcInterrupts();
3043 : :
1723 3044 [ - + ]: 64 : if (CheckForStandbyTrigger())
1723 heikki.linnakangas@i 3045 :UBC 0 : break;
3046 : :
3047 : : /*
3048 : : * Recalculate delayUntil as recovery_min_apply_delay could have
3049 : : * changed while waiting in this loop.
3050 : : */
1723 heikki.linnakangas@i 3051 :CBC 64 : delayUntil = TimestampTzPlusMilliseconds(xtime, recovery_min_apply_delay);
3052 : :
3053 : : /*
3054 : : * Wait for difference between GetCurrentTimestamp() and delayUntil.
3055 : : */
3056 : 64 : msecs = TimestampDifferenceMilliseconds(GetCurrentTimestamp(),
3057 : : delayUntil);
3058 : :
3059 [ + + ]: 64 : if (msecs <= 0)
3060 : 25 : break;
3061 : :
3062 [ - + ]: 39 : elog(DEBUG2, "recovery apply delay %ld milliseconds", msecs);
3063 : :
3064 : 39 : (void) WaitLatch(&XLogRecoveryCtl->recoveryWakeupLatch,
3065 : : WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
3066 : : msecs,
3067 : : WAIT_EVENT_RECOVERY_APPLY_DELAY);
3068 : : }
3069 : 25 : return true;
3070 : : }
3071 : :
3072 : : /*
3073 : : * Get the current state of the recovery pause request.
3074 : : */
3075 : : RecoveryPauseState
3076 : 37 : GetRecoveryPauseState(void)
3077 : : {
3078 : : RecoveryPauseState state;
3079 : :
3080 : 37 : SpinLockAcquire(&XLogRecoveryCtl->info_lck);
3081 : 37 : state = XLogRecoveryCtl->recoveryPauseState;
3082 : 37 : SpinLockRelease(&XLogRecoveryCtl->info_lck);
3083 : :
3084 : 37 : return state;
3085 : : }
3086 : :
3087 : : /*
3088 : : * Set the recovery pause state.
3089 : : *
3090 : : * If recovery pause is requested then sets the recovery pause state to
3091 : : * 'pause requested' if it is not already 'paused'. Otherwise, sets it
3092 : : * to 'not paused' to resume the recovery. The recovery pause will be
3093 : : * confirmed by the ConfirmRecoveryPaused.
3094 : : */
3095 : : void
3096 : 75 : SetRecoveryPause(bool recoveryPause)
3097 : : {
3098 : 75 : SpinLockAcquire(&XLogRecoveryCtl->info_lck);
3099 : :
3100 [ + + ]: 75 : if (!recoveryPause)
3101 : 66 : XLogRecoveryCtl->recoveryPauseState = RECOVERY_NOT_PAUSED;
3102 [ + - ]: 9 : else if (XLogRecoveryCtl->recoveryPauseState == RECOVERY_NOT_PAUSED)
3103 : 9 : XLogRecoveryCtl->recoveryPauseState = RECOVERY_PAUSE_REQUESTED;
3104 : :
3105 : 75 : SpinLockRelease(&XLogRecoveryCtl->info_lck);
3106 : :
3107 [ + + ]: 75 : if (!recoveryPause)
3108 : 66 : ConditionVariableBroadcast(&XLogRecoveryCtl->recoveryNotPausedCV);
3109 : 75 : }
3110 : :
3111 : : /*
3112 : : * Confirm the recovery pause by setting the recovery pause state to
3113 : : * RECOVERY_PAUSED.
3114 : : */
3115 : : static void
3116 : 19 : ConfirmRecoveryPaused(void)
3117 : : {
3118 : : /* If recovery pause is requested then set it paused */
3119 : 19 : SpinLockAcquire(&XLogRecoveryCtl->info_lck);
3120 [ + + ]: 19 : if (XLogRecoveryCtl->recoveryPauseState == RECOVERY_PAUSE_REQUESTED)
3121 : 9 : XLogRecoveryCtl->recoveryPauseState = RECOVERY_PAUSED;
3122 : 19 : SpinLockRelease(&XLogRecoveryCtl->info_lck);
3123 : 19 : }
3124 : :
3125 : :
3126 : : /*
3127 : : * Attempt to read the next XLOG record.
3128 : : *
3129 : : * Before first call, the reader needs to be positioned to the first record
3130 : : * by calling XLogPrefetcherBeginRead().
3131 : : *
3132 : : * If no valid record is available, returns NULL, or fails if emode is PANIC.
3133 : : * (emode must be either PANIC, LOG). In standby mode, retries until a valid
3134 : : * record is available.
3135 : : */
3136 : : static XLogRecord *
1673 tmunro@postgresql.or 3137 : 2943878 : ReadRecord(XLogPrefetcher *xlogprefetcher, int emode,
3138 : : bool fetching_ckpt, TimeLineID replayTLI)
3139 : : {
3140 : : XLogRecord *record;
3141 : 2943878 : XLogReaderState *xlogreader = XLogPrefetcherGetReader(xlogprefetcher);
1723 heikki.linnakangas@i 3142 : 2943878 : XLogPageReadPrivate *private = (XLogPageReadPrivate *) xlogreader->private_data;
3143 : :
365 michael@paquier.xyz 3144 [ + + - + ]: 2943878 : Assert(AmStartupProcess() || !IsUnderPostmaster);
3145 : :
3146 : : /* Pass through parameters to XLogPageRead */
1723 heikki.linnakangas@i 3147 : 2943878 : private->fetching_ckpt = fetching_ckpt;
3148 : 2943878 : private->emode = emode;
364 alvherre@kurilemu.de 3149 : 2943878 : private->randAccess = !XLogRecPtrIsValid(xlogreader->ReadRecPtr);
1723 heikki.linnakangas@i 3150 : 2943878 : private->replayTLI = replayTLI;
3151 : :
3152 : : /* This is the first attempt to read this page. */
3153 : 2943878 : lastSourceFailed = false;
3154 : :
3155 : : for (;;)
3156 : 166 : {
3157 : : char *errormsg;
3158 : :
1673 tmunro@postgresql.or 3159 : 2944044 : record = XLogPrefetcherReadRecord(xlogprefetcher, &errormsg);
1723 heikki.linnakangas@i 3160 [ + + ]: 2943974 : if (record == NULL)
3161 : : {
3162 : : /*
3163 : : * When we find that WAL ends in an incomplete record, keep track
3164 : : * of that record. After recovery is done, we'll write a record
3165 : : * to indicate to downstream WAL readers that that portion is to
3166 : : * be ignored.
3167 : : *
3168 : : * However, when ArchiveRecoveryRequested = true, we're going to
3169 : : * switch to a new timeline at the end of recovery. We will only
3170 : : * copy WAL over to the new timeline up to the end of the last
3171 : : * complete record, so if we did this, we would later create an
3172 : : * overwrite contrecord in the wrong place, breaking everything.
3173 : : */
1529 rhaas@postgresql.org 3174 [ + + ]: 345 : if (!ArchiveRecoveryRequested &&
364 alvherre@kurilemu.de 3175 [ + + ]: 119 : XLogRecPtrIsValid(xlogreader->abortedRecPtr))
3176 : : {
1723 heikki.linnakangas@i 3177 : 12 : abortedRecPtr = xlogreader->abortedRecPtr;
3178 : 12 : missingContrecPtr = xlogreader->missingContrecPtr;
3179 : : }
3180 : :
3181 [ + + ]: 345 : if (readFile >= 0)
3182 : : {
3183 : 319 : close(readFile);
3184 : 319 : readFile = -1;
3185 : : }
3186 : :
3187 : : /*
3188 : : * We only end up here without a message when XLogPageRead()
3189 : : * failed - in that case we already logged something. In
3190 : : * StandbyMode that only happens if we have been triggered, so we
3191 : : * shouldn't loop anymore in that case.
3192 : : */
3193 [ + + ]: 345 : if (errormsg)
3194 [ + + ]: 319 : ereport(emode_for_corrupt_record(emode, xlogreader->EndRecPtr),
3195 : : (errmsg_internal("%s", errormsg) /* already translated */ ));
3196 : : }
3197 : :
3198 : : /*
3199 : : * Check page TLI is one of the expected values.
3200 : : */
3201 [ - + ]: 2943629 : else if (!tliInHistory(xlogreader->latestPageTLI, expectedTLEs))
3202 : : {
3203 : : char fname[MAXFNAMELEN];
3204 : : XLogSegNo segno;
3205 : : int32 offset;
3206 : :
1723 heikki.linnakangas@i 3207 :UBC 0 : XLByteToSeg(xlogreader->latestPagePtr, segno, wal_segment_size);
3208 : 0 : offset = XLogSegmentOffset(xlogreader->latestPagePtr,
3209 : : wal_segment_size);
3210 : 0 : XLogFileName(fname, xlogreader->seg.ws_tli, segno,
3211 : : wal_segment_size);
3212 [ # # ]: 0 : ereport(emode_for_corrupt_record(emode, xlogreader->EndRecPtr),
3213 : : errmsg("unexpected timeline ID %u in WAL segment %s, LSN %X/%08X, offset %u",
3214 : : xlogreader->latestPageTLI,
3215 : : fname,
3216 : : LSN_FORMAT_ARGS(xlogreader->latestPagePtr),
3217 : : offset));
3218 : 0 : record = NULL;
3219 : : }
3220 : :
1723 heikki.linnakangas@i 3221 [ + + ]:CBC 2943974 : if (record)
3222 : : {
3223 : : /* Great, got a record */
3224 : 2943808 : return record;
3225 : : }
3226 : : else
3227 : : {
3228 : : /* No valid record available from this source */
3229 : 345 : lastSourceFailed = true;
3230 : :
3231 : : /*
3232 : : * If archive recovery was requested, but we were still doing
3233 : : * crash recovery, switch to archive recovery and retry using the
3234 : : * offline archive. We have now replayed all the valid WAL in
3235 : : * pg_wal, so we are presumably now consistent.
3236 : : *
3237 : : * We require that there's at least some valid WAL present in
3238 : : * pg_wal, however (!fetching_ckpt). We could recover using the
3239 : : * WAL from the archive, even if pg_wal is completely empty, but
3240 : : * we'd have no idea how far we'd have to replay to reach
3241 : : * consistency. So err on the safe side and give up.
3242 : : */
3243 [ + + + + ]: 345 : if (!InArchiveRecovery && ArchiveRecoveryRequested &&
3244 [ + - ]: 1 : !fetching_ckpt)
3245 : : {
3246 [ - + ]: 1 : ereport(DEBUG1,
3247 : : (errmsg_internal("reached end of WAL in pg_wal, entering archive recovery")));
3248 : 1 : InArchiveRecovery = true;
3249 [ + - ]: 1 : if (StandbyModeRequested)
1368 rhaas@postgresql.org 3250 : 1 : EnableStandbyMode();
3251 : :
1723 heikki.linnakangas@i 3252 : 1 : SwitchIntoArchiveRecovery(xlogreader->EndRecPtr, replayTLI);
3253 : 1 : minRecoveryPoint = xlogreader->EndRecPtr;
3254 : 1 : minRecoveryPointTLI = replayTLI;
3255 : :
3256 : 1 : CheckRecoveryConsistency();
3257 : :
3258 : : /*
3259 : : * Before we retry, reset lastSourceFailed and currentSource
3260 : : * so that we will check the archive next.
3261 : : */
3262 : 1 : lastSourceFailed = false;
3263 : 1 : currentSource = XLOG_FROM_ANY;
3264 : :
3265 : 166 : continue;
3266 : : }
3267 : :
3268 : : /* In standby mode, loop back to retry. Otherwise, give up. */
3269 [ + + + + ]: 344 : if (StandbyMode && !CheckForStandbyTrigger())
3270 : 165 : continue;
3271 : : else
3272 : 179 : return NULL;
3273 : : }
3274 : : }
3275 : : }
3276 : :
3277 : : /*
3278 : : * Read the XLOG page containing targetPagePtr into readBuf (if not read
3279 : : * already). Returns number of bytes read, if the page is read successfully,
3280 : : * or XLREAD_FAIL in case of errors. When errors occur, they are ereport'ed,
3281 : : * but only if they have not been previously reported.
3282 : : *
3283 : : * See XLogReaderRoutine.page_read for more details.
3284 : : *
3285 : : * While prefetching, xlogreader->nonblocking may be set. In that case,
3286 : : * returns XLREAD_WOULDBLOCK if we'd otherwise have to wait for more WAL.
3287 : : *
3288 : : * This is responsible for restoring files from archive as needed, as well
3289 : : * as for waiting for the requested WAL record to arrive in standby mode.
3290 : : *
3291 : : * xlogreader->private_data->emode specifies the log level used for reporting
3292 : : * "file not found" or "end of WAL" situations in archive recovery, or in
3293 : : * standby mode when promotion is triggered. If set to WARNING or below,
3294 : : * XLogPageRead() returns XLREAD_FAIL in those situations, on higher log
3295 : : * levels the ereport() won't return.
3296 : : *
3297 : : * In standby mode, if after a successful return of XLogPageRead() the
3298 : : * caller finds the record it's interested in to be broken, it should
3299 : : * ereport the error with the level determined by
3300 : : * emode_for_corrupt_record(), and then set lastSourceFailed
3301 : : * and call XLogPageRead() again with the same arguments. This lets
3302 : : * XLogPageRead() to try fetching the record from another source, or to
3303 : : * sleep and retry.
3304 : : */
3305 : : static int
3306 : 1512708 : XLogPageRead(XLogReaderState *xlogreader, XLogRecPtr targetPagePtr, int reqLen,
3307 : : XLogRecPtr targetRecPtr, char *readBuf)
3308 : : {
3309 : 1512708 : XLogPageReadPrivate *private =
3310 : : (XLogPageReadPrivate *) xlogreader->private_data;
3311 : 1512708 : int emode = private->emode;
3312 : : uint32 targetPageOff;
3313 : : XLogSegNo targetSegNo PG_USED_FOR_ASSERTS_ONLY;
3314 : : ssize_t r;
3315 : : instr_time io_start;
3316 : :
365 michael@paquier.xyz 3317 [ + + - + ]: 1512708 : Assert(AmStartupProcess() || !IsUnderPostmaster);
3318 : :
1723 heikki.linnakangas@i 3319 : 1512708 : XLByteToSeg(targetPagePtr, targetSegNo, wal_segment_size);
3320 : 1512708 : targetPageOff = XLogSegmentOffset(targetPagePtr, wal_segment_size);
3321 : :
3322 : : /*
3323 : : * See if we need to switch to a new segment because the requested record
3324 : : * is not in the currently open one.
3325 : : */
3326 [ + + ]: 1512708 : if (readFile >= 0 &&
3327 [ + + ]: 1510903 : !XLByteInSeg(targetPagePtr, readSegNo, wal_segment_size))
3328 : : {
3329 : : /*
3330 : : * Request a restartpoint if we've replayed too much xlog since the
3331 : : * last one.
3332 : : */
3333 [ + + + - ]: 1384 : if (ArchiveRecoveryRequested && IsUnderPostmaster)
3334 : : {
3335 [ + + ]: 1364 : if (XLogCheckpointNeeded(readSegNo))
3336 : : {
3337 : 1215 : (void) GetRedoRecPtr();
3338 [ + + ]: 1215 : if (XLogCheckpointNeeded(readSegNo))
3339 : 1205 : RequestCheckpoint(CHECKPOINT_CAUSE_XLOG);
3340 : : }
3341 : : }
3342 : :
3343 : 1384 : close(readFile);
3344 : 1384 : readFile = -1;
3345 : 1384 : readSource = XLOG_FROM_ANY;
3346 : : }
3347 : :
3348 : 1512708 : XLByteToSeg(targetPagePtr, readSegNo, wal_segment_size);
3349 : :
3350 : 1512713 : retry:
3351 : : /* See if we need to retrieve more data */
3352 [ + + ]: 1512713 : if (readFile < 0 ||
3353 [ + + ]: 1509519 : (readSource == XLOG_FROM_STREAM &&
3354 [ + + ]: 1491905 : flushedUpto < targetPagePtr + reqLen))
3355 : : {
1673 tmunro@postgresql.or 3356 [ + + ]: 33241 : if (readFile >= 0 &&
3357 [ + + ]: 30047 : xlogreader->nonblocking &&
3358 [ + - ]: 14829 : readSource == XLOG_FROM_STREAM &&
3359 [ + - ]: 14829 : flushedUpto < targetPagePtr + reqLen)
3360 : 14829 : return XLREAD_WOULDBLOCK;
3361 : :
3362 [ + + + - ]: 18342 : switch (WaitForWALToBecomeAvailable(targetPagePtr + reqLen,
3363 : 18412 : private->randAccess,
3364 : 18412 : private->fetching_ckpt,
3365 : : targetRecPtr,
3366 : : private->replayTLI,
3367 : : xlogreader->EndRecPtr,
3368 : 18412 : xlogreader->nonblocking))
3369 : : {
3370 : 310 : case XLREAD_WOULDBLOCK:
3371 : 310 : return XLREAD_WOULDBLOCK;
3372 : 50 : case XLREAD_FAIL:
3373 [ - + ]: 50 : if (readFile >= 0)
1673 tmunro@postgresql.or 3374 :UBC 0 : close(readFile);
1673 tmunro@postgresql.or 3375 :CBC 50 : readFile = -1;
3376 : 50 : readLen = 0;
3377 : 50 : readSource = XLOG_FROM_ANY;
3378 : 50 : return XLREAD_FAIL;
3379 : 17982 : case XLREAD_SUCCESS:
3380 : 17982 : break;
3381 : : }
3382 : : }
3383 : :
3384 : : /*
3385 : : * At this point, we have the right segment open and if we're streaming we
3386 : : * know the requested record is in it.
3387 : : */
1723 heikki.linnakangas@i 3388 [ - + ]: 1497454 : Assert(readFile != -1);
3389 : :
3390 : : /*
3391 : : * If the current segment is being streamed from the primary, calculate
3392 : : * how much of the current page we have received already. We know the
3393 : : * requested record has been received, but this is for the benefit of
3394 : : * future calls, to allow quick exit at the top of this function.
3395 : : */
3396 [ + + ]: 1497454 : if (readSource == XLOG_FROM_STREAM)
3397 : : {
3398 [ + + ]: 1478006 : if (((targetPagePtr) / XLOG_BLCKSZ) != (flushedUpto / XLOG_BLCKSZ))
3399 : 1466466 : readLen = XLOG_BLCKSZ;
3400 : : else
3401 : 11540 : readLen = XLogSegmentOffset(flushedUpto, wal_segment_size) -
3402 : : targetPageOff;
3403 : : }
3404 : : else
3405 : 19448 : readLen = XLOG_BLCKSZ;
3406 : :
3407 : : /* Read the requested page */
3408 : 1497454 : readOff = targetPageOff;
3409 : :
3410 : : /* Measure I/O timing when reading segment */
617 michael@paquier.xyz 3411 : 1497454 : io_start = pgstat_prepare_io_time(track_wal_io_timing);
3412 : :
1723 heikki.linnakangas@i 3413 : 1497454 : pgstat_report_wait_start(WAIT_EVENT_WAL_READ);
357 michael@paquier.xyz 3414 : 1497454 : r = pg_pread(readFile, readBuf, XLOG_BLCKSZ, (pgoff_t) readOff);
1723 heikki.linnakangas@i 3415 [ - + ]: 1497454 : if (r != XLOG_BLCKSZ)
3416 : : {
3417 : : char fname[MAXFNAMELEN];
1723 heikki.linnakangas@i 3418 :UBC 0 : int save_errno = errno;
3419 : :
3420 : 0 : pgstat_report_wait_end();
3421 : :
3422 : : /* Count I/O stats only for successful short reads */
141 michael@paquier.xyz 3423 [ # # ]: 0 : if (r > 0)
3424 : 0 : pgstat_count_io_op_time(IOOBJECT_WAL, IOCONTEXT_NORMAL, IOOP_READ,
3425 : : io_start, 1, r);
3426 : :
1723 heikki.linnakangas@i 3427 : 0 : XLogFileName(fname, curFileTLI, readSegNo, wal_segment_size);
3428 [ # # ]: 0 : if (r < 0)
3429 : : {
3430 : 0 : errno = save_errno;
3431 [ # # ]: 0 : ereport(emode_for_corrupt_record(emode, targetPagePtr + reqLen),
3432 : : (errcode_for_file_access(),
3433 : : errmsg("could not read from WAL segment %s, LSN %X/%08X, offset %u: %m",
3434 : : fname, LSN_FORMAT_ARGS(targetPagePtr),
3435 : : readOff)));
3436 : : }
3437 : : else
3438 [ # # ]: 0 : ereport(emode_for_corrupt_record(emode, targetPagePtr + reqLen),
3439 : : (errcode(ERRCODE_DATA_CORRUPTED),
3440 : : errmsg("could not read from WAL segment %s, LSN %X/%08X, offset %u: read %zd of %zu",
3441 : : fname, LSN_FORMAT_ARGS(targetPagePtr),
3442 : : readOff, r, (Size) XLOG_BLCKSZ)));
3443 : 0 : goto next_record_is_invalid;
3444 : : }
1723 heikki.linnakangas@i 3445 :CBC 1497454 : pgstat_report_wait_end();
3446 : :
639 michael@paquier.xyz 3447 : 1497454 : pgstat_count_io_op_time(IOOBJECT_WAL, IOCONTEXT_NORMAL, IOOP_READ,
3448 : : io_start, 1, r);
3449 : :
1723 heikki.linnakangas@i 3450 [ - + ]: 1497454 : Assert(targetSegNo == readSegNo);
3451 [ - + ]: 1497454 : Assert(targetPageOff == readOff);
3452 [ - + ]: 1497454 : Assert(reqLen <= readLen);
3453 : :
3454 : 1497454 : xlogreader->seg.ws_tli = curFileTLI;
3455 : :
3456 : : /*
3457 : : * Check the page header immediately, so that we can retry immediately if
3458 : : * it's not valid. This may seem unnecessary, because ReadPageInternal()
3459 : : * validates the page header anyway, and would propagate the failure up to
3460 : : * ReadRecord(), which would retry. However, there's a corner case with
3461 : : * continuation records, if a record is split across two pages such that
3462 : : * we would need to read the two pages from different sources across two
3463 : : * WAL segments.
3464 : : *
3465 : : * The first page is only available locally, in pg_wal, because it's
3466 : : * already been recycled on the primary. The second page, however, is not
3467 : : * present in pg_wal, and we should stream it from the primary. There is a
3468 : : * recycled WAL segment present in pg_wal, with garbage contents, however.
3469 : : * We would read the first page from the local WAL segment, but when
3470 : : * reading the second page, we would read the bogus, recycled, WAL
3471 : : * segment. If we didn't catch that case here, we would never recover,
3472 : : * because ReadRecord() would retry reading the whole record from the
3473 : : * beginning.
3474 : : *
3475 : : * Of course, this only catches errors in the page header, which is what
3476 : : * happens in the case of a recycled WAL segment. Other kinds of errors or
3477 : : * corruption still has the same problem. But this at least fixes the
3478 : : * common case, which can happen as part of normal operation.
3479 : : *
3480 : : * Validating the page header is cheap enough that doing it twice
3481 : : * shouldn't be a big deal from a performance point of view.
3482 : : *
3483 : : * When not in standby mode, an invalid page header should cause recovery
3484 : : * to end, not retry reading the page, so we don't need to validate the
3485 : : * page header here for the retry. Instead, ReadPageInternal() is
3486 : : * responsible for the validation.
3487 : : */
3488 [ + + ]: 1497454 : if (StandbyMode &&
654 michael@paquier.xyz 3489 [ + + ]: 1486801 : (targetPagePtr % wal_segment_size) == 0 &&
1723 heikki.linnakangas@i 3490 [ + + ]: 1511 : !XLogReaderValidatePageHeader(xlogreader, targetPagePtr, readBuf))
3491 : : {
3492 : : /*
3493 : : * Emit this error right now then retry this page immediately. Use
3494 : : * errmsg_internal() because the message was already translated.
3495 : : */
3496 [ + - ]: 6 : if (xlogreader->errormsg_buf[0])
3497 [ + + ]: 6 : ereport(emode_for_corrupt_record(emode, xlogreader->EndRecPtr),
3498 : : (errmsg_internal("%s", xlogreader->errormsg_buf)));
3499 : :
3500 : : /* reset any error XLogReaderValidatePageHeader() might have set */
1524 tmunro@postgresql.or 3501 : 6 : XLogReaderResetError(xlogreader);
1723 heikki.linnakangas@i 3502 : 6 : goto next_record_is_invalid;
3503 : : }
3504 : :
3505 : 1497448 : return readLen;
3506 : :
3507 : 6 : next_record_is_invalid:
3508 : :
3509 : : /*
3510 : : * If we're reading ahead, give up fast. Retries and error reporting will
3511 : : * be handled by a later read when recovery catches up to this point.
3512 : : */
1524 tmunro@postgresql.or 3513 [ + + ]: 6 : if (xlogreader->nonblocking)
3514 : 1 : return XLREAD_WOULDBLOCK;
3515 : :
1723 heikki.linnakangas@i 3516 : 5 : lastSourceFailed = true;
3517 : :
3518 [ + - ]: 5 : if (readFile >= 0)
3519 : 5 : close(readFile);
3520 : 5 : readFile = -1;
3521 : 5 : readLen = 0;
3522 : 5 : readSource = XLOG_FROM_ANY;
3523 : :
3524 : : /* In standby-mode, keep trying */
3525 [ + - ]: 5 : if (StandbyMode)
3526 : 5 : goto retry;
3527 : : else
1673 tmunro@postgresql.or 3528 :UBC 0 : return XLREAD_FAIL;
3529 : : }
3530 : :
3531 : : /*
3532 : : * Open the WAL segment containing WAL location 'RecPtr'.
3533 : : *
3534 : : * The segment can be fetched via restore_command, or via walreceiver having
3535 : : * streamed the record, or it can already be present in pg_wal. Checking
3536 : : * pg_wal is mainly for crash recovery, but it will be polled in standby mode
3537 : : * too, in case someone copies a new segment directly to pg_wal. That is not
3538 : : * documented or recommended, though.
3539 : : *
3540 : : * If 'fetching_ckpt' is true, we're fetching a checkpoint record, and should
3541 : : * prepare to read WAL starting from RedoStartLSN after this.
3542 : : *
3543 : : * 'RecPtr' might not point to the beginning of the record we're interested
3544 : : * in, it might also point to the page or segment header. In that case,
3545 : : * 'tliRecPtr' is the position of the WAL record we're interested in. It is
3546 : : * used to decide which timeline to stream the requested WAL from.
3547 : : *
3548 : : * 'replayLSN' is the current replay LSN, so that if we scan for new
3549 : : * timelines, we can reject a switch to a timeline that branched off before
3550 : : * this point.
3551 : : *
3552 : : * If the record is not immediately available, the function returns XLREAD_FAIL
3553 : : * if we're not in standby mode. In standby mode, the function waits for it to
3554 : : * become available.
3555 : : *
3556 : : * When the requested record becomes available, the function opens the file
3557 : : * containing it (if not open already), and returns XLREAD_SUCCESS. When end
3558 : : * of standby mode is triggered by the user, and there is no more WAL
3559 : : * available, returns XLREAD_FAIL.
3560 : : *
3561 : : * If nonblocking is true, then give up immediately if we can't satisfy the
3562 : : * request, returning XLREAD_WOULDBLOCK instead of waiting.
3563 : : */
3564 : : static XLogPageReadResult
1723 heikki.linnakangas@i 3565 :CBC 18412 : WaitForWALToBecomeAvailable(XLogRecPtr RecPtr, bool randAccess,
3566 : : bool fetching_ckpt, XLogRecPtr tliRecPtr,
3567 : : TimeLineID replayTLI, XLogRecPtr replayLSN,
3568 : : bool nonblocking)
3569 : : {
3570 : : static TimestampTz last_fail_time = 0;
3571 : : TimestampTz now;
3572 : 18412 : bool streaming_reply_sent = false;
3573 : :
3574 : : /*-------
3575 : : * Standby mode is implemented by a state machine:
3576 : : *
3577 : : * 1. Read from either archive or pg_wal (XLOG_FROM_ARCHIVE), or just
3578 : : * pg_wal (XLOG_FROM_PG_WAL)
3579 : : * 2. Check for promotion trigger request
3580 : : * 3. Read from primary server via walreceiver (XLOG_FROM_STREAM)
3581 : : * 4. Rescan timelines
3582 : : * 5. Sleep wal_retrieve_retry_interval milliseconds, and loop back to 1.
3583 : : *
3584 : : * Failure to read from the current source advances the state machine to
3585 : : * the next state.
3586 : : *
3587 : : * 'currentSource' indicates the current state. There are no currentSource
3588 : : * values for "check trigger", "rescan timelines", and "sleep" states,
3589 : : * those actions are taken when reading from the previous source fails, as
3590 : : * part of advancing to the next state.
3591 : : *
3592 : : * If standby mode is turned off while reading WAL from stream, we move
3593 : : * to XLOG_FROM_ARCHIVE and reset lastSourceFailed, to force fetching
3594 : : * the files (which would be required at end of recovery, e.g., timeline
3595 : : * history file) from archive or pg_wal. We don't need to kill WAL receiver
3596 : : * here because it's already stopped when standby mode is turned off at
3597 : : * the end of recovery.
3598 : : *-------
3599 : : */
3600 [ + + ]: 18412 : if (!InArchiveRecovery)
3601 : 1102 : currentSource = XLOG_FROM_PG_WAL;
3602 [ + + ]: 17310 : else if (currentSource == XLOG_FROM_ANY ||
3603 [ + + - + ]: 17158 : (!StandbyMode && currentSource == XLOG_FROM_STREAM))
3604 : : {
3605 : 152 : lastSourceFailed = false;
3606 : 152 : currentSource = XLOG_FROM_ARCHIVE;
3607 : : }
3608 : :
3609 : : for (;;)
3610 : 15507 : {
3611 : 33919 : XLogSource oldSource = currentSource;
3612 : 33919 : bool startWalReceiver = false;
3613 : :
3614 : : /*
3615 : : * First check if we failed to read from the current source, and
3616 : : * advance the state machine if so. The failure to read might've
3617 : : * happened outside this function, e.g when a CRC check fails on a
3618 : : * record, or within this loop.
3619 : : */
3620 [ + + ]: 33919 : if (lastSourceFailed)
3621 : : {
3622 : : /*
3623 : : * Don't allow any retry loops to occur during nonblocking
3624 : : * readahead. Let the caller process everything that has been
3625 : : * decoded already first.
3626 : : */
1673 tmunro@postgresql.or 3627 [ + + ]: 641 : if (nonblocking)
3628 : 97 : return XLREAD_WOULDBLOCK;
3629 : :
1723 heikki.linnakangas@i 3630 [ + + - ]: 544 : switch (currentSource)
3631 : : {
3632 : 325 : case XLOG_FROM_ARCHIVE:
3633 : : case XLOG_FROM_PG_WAL:
3634 : :
3635 : : /*
3636 : : * Check to see if promotion is requested. Note that we do
3637 : : * this only after failure, so when you promote, we still
3638 : : * finish replaying as much as we can from archive and
3639 : : * pg_wal before failover.
3640 : : */
3641 [ + + + + ]: 325 : if (StandbyMode && CheckForStandbyTrigger())
3642 : : {
3643 : 16 : XLogShutdownWalRcv();
1673 tmunro@postgresql.or 3644 : 16 : return XLREAD_FAIL;
3645 : : }
3646 : :
3647 : : /*
3648 : : * Not in standby mode, and we've now tried the archive
3649 : : * and pg_wal.
3650 : : */
1723 heikki.linnakangas@i 3651 [ + + ]: 309 : if (!StandbyMode)
1673 tmunro@postgresql.or 3652 : 34 : return XLREAD_FAIL;
3653 : :
3654 : : /*
3655 : : * Move to XLOG_FROM_STREAM state, and set to start a
3656 : : * walreceiver if necessary.
3657 : : */
1723 heikki.linnakangas@i 3658 : 275 : currentSource = XLOG_FROM_STREAM;
3659 : 275 : startWalReceiver = true;
3660 : 275 : break;
3661 : :
3662 : 219 : case XLOG_FROM_STREAM:
3663 : :
3664 : : /*
3665 : : * Failure while streaming. Most likely, we got here
3666 : : * because streaming replication was terminated, or
3667 : : * promotion was triggered. But we also get here if we
3668 : : * find an invalid record in the WAL streamed from the
3669 : : * primary, in which case something is seriously wrong.
3670 : : * There's little chance that the problem will just go
3671 : : * away, but PANIC is not good for availability either,
3672 : : * especially in hot standby mode. So, we treat that the
3673 : : * same as disconnection, and retry from archive/pg_wal
3674 : : * again. The WAL in the archive should be identical to
3675 : : * what was streamed, so it's unlikely that it helps, but
3676 : : * one can hope...
3677 : : */
3678 : :
3679 : : /*
3680 : : * We should be able to move to XLOG_FROM_STREAM only in
3681 : : * standby mode.
3682 : : */
3683 [ - + ]: 219 : Assert(StandbyMode);
3684 : :
3685 : : /*
3686 : : * Before we leave XLOG_FROM_STREAM state, make sure that
3687 : : * walreceiver is not active, so that it won't overwrite
3688 : : * WAL that we restore from archive.
3689 : : *
3690 : : * If walreceiver is actively streaming (or attempting to
3691 : : * connect), we must shut it down. However, if it's
3692 : : * already in WAITING state (e.g., due to timeline
3693 : : * divergence), we only need to reset the install flag to
3694 : : * allow archive restoration.
3695 : : */
366 michael@paquier.xyz 3696 [ + + ]: 219 : if (WalRcvStreaming())
3697 : 40 : XLogShutdownWalRcv();
3698 : : else
3699 : : {
3700 : : /*
3701 : : * WALRCV_STOPPING state is a transient state while
3702 : : * the startup process is in ShutdownWalRcv(). It
3703 : : * should never appear here since we would be waiting
3704 : : * for the walreceiver to reach WALRCV_STOPPED in that
3705 : : * case.
3706 : : */
3707 [ - + ]: 179 : Assert(WalRcvGetState() != WALRCV_STOPPING);
3708 : 179 : ResetInstallXLogFileSegmentActive();
3709 : : }
3710 : :
3711 : : /*
3712 : : * Before we sleep, re-scan for possible new timelines if
3713 : : * we were requested to recover to the latest timeline.
3714 : : */
1723 heikki.linnakangas@i 3715 [ + - ]: 219 : if (recoveryTargetTimeLineGoal == RECOVERY_TARGET_TIMELINE_LATEST)
3716 : : {
3717 [ + + ]: 219 : if (rescanLatestTimeLine(replayTLI, replayLSN))
3718 : : {
3719 : 9 : currentSource = XLOG_FROM_ARCHIVE;
3720 : 9 : break;
3721 : : }
3722 : : }
3723 : :
3724 : : /*
3725 : : * XLOG_FROM_STREAM is the last state in our state
3726 : : * machine, so we've exhausted all the options for
3727 : : * obtaining the requested WAL. We're going to loop back
3728 : : * and retry from the archive, but if it hasn't been long
3729 : : * since last attempt, sleep wal_retrieve_retry_interval
3730 : : * milliseconds to avoid busy-waiting.
3731 : : */
3732 : 210 : now = GetCurrentTimestamp();
3733 [ + + ]: 210 : if (!TimestampDifferenceExceeds(last_fail_time, now,
3734 : : wal_retrieve_retry_interval))
3735 : : {
3736 : : long wait_time;
3737 : :
3738 : 232 : wait_time = wal_retrieve_retry_interval -
3739 : 116 : TimestampDifferenceMilliseconds(last_fail_time, now);
3740 : :
486 alvherre@kurilemu.de 3741 [ + - ]: 116 : elog(LOG, "waiting for WAL to become available at %X/%08X",
3742 : : LSN_FORMAT_ARGS(RecPtr));
3743 : :
3744 : : /* Do background tasks that might benefit us later. */
1437 tgl@sss.pgh.pa.us 3745 : 116 : KnownAssignedTransactionIdsIdleMaintenance();
3746 : :
1723 heikki.linnakangas@i 3747 : 116 : (void) WaitLatch(&XLogRecoveryCtl->recoveryWakeupLatch,
3748 : : WL_LATCH_SET | WL_TIMEOUT |
3749 : : WL_EXIT_ON_PM_DEATH,
3750 : : wait_time,
3751 : : WAIT_EVENT_RECOVERY_RETRIEVE_RETRY_INTERVAL);
3752 : 116 : ResetLatch(&XLogRecoveryCtl->recoveryWakeupLatch);
3753 : 116 : now = GetCurrentTimestamp();
3754 : :
3755 : : /* Handle interrupt signals of startup process */
610 3756 : 116 : ProcessStartupProcInterrupts();
3757 : : }
1723 3758 : 194 : last_fail_time = now;
3759 : 194 : currentSource = XLOG_FROM_ARCHIVE;
3760 : 194 : break;
3761 : :
1723 heikki.linnakangas@i 3762 :UBC 0 : default:
3763 [ # # ]: 0 : elog(ERROR, "unexpected WAL source %d", currentSource);
3764 : : }
3765 : : }
1723 heikki.linnakangas@i 3766 [ + + ]:CBC 33278 : else if (currentSource == XLOG_FROM_PG_WAL)
3767 : : {
3768 : : /*
3769 : : * We just successfully read a file in pg_wal. We prefer files in
3770 : : * the archive over ones in pg_wal, so try the next file again
3771 : : * from the archive first.
3772 : : */
3773 [ - + ]: 1098 : if (InArchiveRecovery)
1723 heikki.linnakangas@i 3774 :UBC 0 : currentSource = XLOG_FROM_ARCHIVE;
3775 : : }
3776 : :
1723 heikki.linnakangas@i 3777 [ + + ]:CBC 33756 : if (currentSource != oldSource)
3778 [ + + + - ]: 478 : elog(DEBUG2, "switched WAL source from %s to %s after %s",
3779 : : xlogSourceNames[oldSource], xlogSourceNames[currentSource],
3780 : : lastSourceFailed ? "failure" : "success");
3781 : :
3782 : : /*
3783 : : * We've now handled possible failure. Try to read from the chosen
3784 : : * source.
3785 : : */
3786 : 33756 : lastSourceFailed = false;
3787 : :
3788 [ + + - ]: 33756 : switch (currentSource)
3789 : : {
3790 : 2062 : case XLOG_FROM_ARCHIVE:
3791 : : case XLOG_FROM_PG_WAL:
3792 : :
3793 : : /*
3794 : : * WAL receiver must not be running when reading WAL from
3795 : : * archive or pg_wal.
3796 : : */
3797 [ - + ]: 2062 : Assert(!WalRcvStreaming());
3798 : :
3799 : : /* Close any old file we might have open. */
3800 [ + + ]: 2062 : if (readFile >= 0)
3801 : : {
3802 : 111 : close(readFile);
3803 : 111 : readFile = -1;
3804 : : }
3805 : : /* Reset curFileTLI if random fetch. */
3806 [ + + ]: 2062 : if (randAccess)
3807 : 1310 : curFileTLI = 0;
3808 : :
3809 : : /*
3810 : : * Try to restore the file from archive, or read an existing
3811 : : * file from pg_wal.
3812 : : */
786 michael@paquier.xyz 3813 : 2062 : readFile = XLogFileReadAnyTLI(readSegNo,
1723 heikki.linnakangas@i 3814 [ + + ]: 2062 : currentSource == XLOG_FROM_ARCHIVE ? XLOG_FROM_ANY :
3815 : : currentSource);
3816 [ + + ]: 2062 : if (readFile >= 0)
1673 tmunro@postgresql.or 3817 : 1834 : return XLREAD_SUCCESS; /* success! */
3818 : :
3819 : : /*
3820 : : * Nope, not found in archive or pg_wal.
3821 : : */
1723 heikki.linnakangas@i 3822 : 228 : lastSourceFailed = true;
3823 : 228 : break;
3824 : :
3825 : 31694 : case XLOG_FROM_STREAM:
3826 : : {
3827 : : bool havedata;
3828 : :
3829 : : /*
3830 : : * We should be able to move to XLOG_FROM_STREAM only in
3831 : : * standby mode.
3832 : : */
3833 [ - + ]: 31694 : Assert(StandbyMode);
3834 : :
3835 : : /*
3836 : : * First, shutdown walreceiver if its restart has been
3837 : : * requested -- but no point if we're already slated for
3838 : : * starting it.
3839 : : */
3840 [ + + + - ]: 31694 : if (pendingWalRcvRestart && !startWalReceiver)
3841 : : {
3842 : 9 : XLogShutdownWalRcv();
3843 : :
3844 : : /*
3845 : : * Re-scan for possible new timelines if we were
3846 : : * requested to recover to the latest timeline.
3847 : : */
3848 [ + - ]: 9 : if (recoveryTargetTimeLineGoal ==
3849 : : RECOVERY_TARGET_TIMELINE_LATEST)
3850 : 9 : rescanLatestTimeLine(replayTLI, replayLSN);
3851 : :
3852 : 9 : startWalReceiver = true;
3853 : : }
3854 : 31694 : pendingWalRcvRestart = false;
3855 : :
3856 : : /*
3857 : : * Launch walreceiver if needed.
3858 : : *
3859 : : * If fetching_ckpt is true, RecPtr points to the initial
3860 : : * checkpoint location. In that case, we use RedoStartLSN
3861 : : * as the streaming start position instead of RecPtr, so
3862 : : * that when we later jump backwards to start redo at
3863 : : * RedoStartLSN, we will have the logs streamed already.
3864 : : */
3865 [ + + + - ]: 31694 : if (startWalReceiver &&
3866 [ + + ]: 284 : PrimaryConnInfo && strcmp(PrimaryConnInfo, "") != 0)
3867 : : {
3868 : : XLogRecPtr ptr;
3869 : : TimeLineID tli;
3870 : :
3871 [ - + ]: 228 : if (fetching_ckpt)
3872 : : {
1723 heikki.linnakangas@i 3873 :UBC 0 : ptr = RedoStartLSN;
3874 : 0 : tli = RedoStartTLI;
3875 : : }
3876 : : else
3877 : : {
1723 heikki.linnakangas@i 3878 :CBC 228 : ptr = RecPtr;
3879 : :
3880 : : /*
3881 : : * Use the record begin position to determine the
3882 : : * TLI, rather than the position we're reading.
3883 : : */
3884 : 228 : tli = tliOfPointInHistory(tliRecPtr, expectedTLEs);
3885 : :
3886 [ + - - + ]: 228 : if (curFileTLI > 0 && tli < curFileTLI)
486 alvherre@kurilemu.de 3887 [ # # ]:UBC 0 : elog(ERROR, "according to history file, WAL location %X/%08X belongs to timeline %u, but previous recovered WAL file came from timeline %u",
3888 : : LSN_FORMAT_ARGS(tliRecPtr),
3889 : : tli, curFileTLI);
3890 : : }
1723 heikki.linnakangas@i 3891 :CBC 228 : curFileTLI = tli;
3892 : 228 : SetInstallXLogFileSegmentActive();
3893 : 228 : RequestXLogStreaming(tli, ptr, PrimaryConnInfo,
3894 : : PrimarySlotName,
3895 : : wal_receiver_create_temp_slot);
280 alvherre@kurilemu.de 3896 : 228 : flushedUpto = InvalidXLogRecPtr;
3897 : : }
3898 : :
3899 : : /*
3900 : : * Check if WAL receiver is active or wait to start up.
3901 : : */
1723 heikki.linnakangas@i 3902 [ + + ]: 31694 : if (!WalRcvStreaming())
3903 : : {
3904 : 179 : lastSourceFailed = true;
3905 : 179 : break;
3906 : : }
3907 : :
3908 : : /*
3909 : : * Walreceiver is active, so see if new data has arrived.
3910 : : *
3911 : : * We only advance XLogReceiptTime when we obtain fresh
3912 : : * WAL from walreceiver and observe that we had already
3913 : : * processed everything before the most recent "chunk"
3914 : : * that it flushed to disk. In steady state where we are
3915 : : * keeping up with the incoming data, XLogReceiptTime will
3916 : : * be updated on each cycle. When we are behind,
3917 : : * XLogReceiptTime will not advance, so the grace time
3918 : : * allotted to conflicting queries will decrease.
3919 : : */
3920 [ + + ]: 31515 : if (RecPtr < flushedUpto)
3921 : 1722 : havedata = true;
3922 : : else
3923 : : {
3924 : : XLogRecPtr latestChunkStart;
3925 : :
3926 : 29793 : flushedUpto = GetWalRcvFlushRecPtr(&latestChunkStart, &receiveTLI);
3927 [ + + + - ]: 29793 : if (RecPtr < flushedUpto && receiveTLI == curFileTLI)
3928 : : {
3929 : 15507 : havedata = true;
3930 [ + + ]: 15507 : if (latestChunkStart <= RecPtr)
3931 : : {
3932 : 11018 : XLogReceiptTime = GetCurrentTimestamp();
3933 : 11018 : SetCurrentChunkStartTime(XLogReceiptTime);
3934 : : }
3935 : : }
3936 : : else
3937 : 14286 : havedata = false;
3938 : : }
3939 [ + + ]: 31515 : if (havedata)
3940 : : {
3941 : : /*
3942 : : * Great, streamed far enough. Open the file if it's
3943 : : * not open already. Also read the timeline history
3944 : : * file if we haven't initialized timeline history
3945 : : * yet; it should be streamed over and present in
3946 : : * pg_wal by now. Use XLOG_FROM_STREAM so that source
3947 : : * info is set correctly and XLogReceiptTime isn't
3948 : : * changed.
3949 : : *
3950 : : * NB: We must set readTimeLineHistory based on
3951 : : * recoveryTargetTLI, not receiveTLI. Normally they'll
3952 : : * be the same, but if recovery_target_timeline is
3953 : : * 'latest' and archiving is configured, then it's
3954 : : * possible that we managed to retrieve one or more
3955 : : * new timeline history files from the archive,
3956 : : * updating recoveryTargetTLI.
3957 : : */
3958 [ + + ]: 17229 : if (readFile < 0)
3959 : : {
3960 [ - + ]: 1081 : if (!expectedTLEs)
1723 heikki.linnakangas@i 3961 :UBC 0 : expectedTLEs = readTimeLineHistory(recoveryTargetTLI);
786 michael@paquier.xyz 3962 :CBC 1081 : readFile = XLogFileRead(readSegNo, receiveTLI,
3963 : : XLOG_FROM_STREAM, false);
1723 heikki.linnakangas@i 3964 [ - + ]: 1081 : Assert(readFile >= 0);
3965 : : }
3966 : : else
3967 : : {
3968 : : /* just make sure source info is correct... */
3969 : 16148 : readSource = XLOG_FROM_STREAM;
3970 : 16148 : XLogReceiptSource = XLOG_FROM_STREAM;
1673 tmunro@postgresql.or 3971 : 16148 : return XLREAD_SUCCESS;
3972 : : }
1723 heikki.linnakangas@i 3973 : 1081 : break;
3974 : : }
3975 : :
3976 : : /* In nonblocking mode, return rather than sleeping. */
1673 tmunro@postgresql.or 3977 [ + + ]: 14286 : if (nonblocking)
3978 : 213 : return XLREAD_WOULDBLOCK;
3979 : :
3980 : : /*
3981 : : * Data not here yet. Check for trigger, then wait for
3982 : : * walreceiver to wake us up when new WAL arrives.
3983 : : */
1723 heikki.linnakangas@i 3984 [ + + ]: 14073 : if (CheckForStandbyTrigger())
3985 : : {
3986 : : /*
3987 : : * Note that we don't return XLREAD_FAIL immediately
3988 : : * here. After being triggered, we still want to
3989 : : * replay all the WAL that was already streamed. It's
3990 : : * in pg_wal now, so we just treat this as a failure,
3991 : : * and the state machine will move on to replay the
3992 : : * streamed WAL from pg_wal, and then recheck the
3993 : : * trigger and exit replay.
3994 : : */
3995 : 40 : lastSourceFailed = true;
3996 : 40 : break;
3997 : : }
3998 : :
3999 : : /*
4000 : : * Since we have replayed everything we have received so
4001 : : * far and are about to start waiting for more WAL, let's
4002 : : * tell the upstream server our replay location now so
4003 : : * that pg_stat_replication doesn't show stale
4004 : : * information.
4005 : : */
4006 [ + + ]: 14033 : if (!streaming_reply_sent)
4007 : : {
224 fujii@postgresql.org 4008 : 11601 : WalRcvRequestApplyReply();
1723 heikki.linnakangas@i 4009 : 11601 : streaming_reply_sent = true;
4010 : : }
4011 : :
4012 : : /* Do any background tasks that might benefit us later. */
1437 tgl@sss.pgh.pa.us 4013 : 14033 : KnownAssignedTransactionIdsIdleMaintenance();
4014 : :
4015 : : /* Update pg_stat_recovery_prefetch before sleeping. */
1673 tmunro@postgresql.or 4016 : 14033 : XLogPrefetcherComputeStats(xlogprefetcher);
4017 : :
4018 : : /*
4019 : : * Wait for more WAL to arrive, when we will be woken
4020 : : * immediately by the WAL receiver.
4021 : : */
1723 heikki.linnakangas@i 4022 : 14033 : (void) WaitLatch(&XLogRecoveryCtl->recoveryWakeupLatch,
4023 : : WL_LATCH_SET | WL_EXIT_ON_PM_DEATH,
4024 : : -1L,
4025 : : WAIT_EVENT_RECOVERY_WAL_STREAM);
4026 : 14033 : ResetLatch(&XLogRecoveryCtl->recoveryWakeupLatch);
4027 : 14033 : break;
4028 : : }
4029 : :
1723 heikki.linnakangas@i 4030 :UBC 0 : default:
4031 [ # # ]: 0 : elog(ERROR, "unexpected WAL source %d", currentSource);
4032 : : }
4033 : :
4034 : : /*
4035 : : * Check for recovery pause here so that we can confirm more quickly
4036 : : * that a requested pause has actually taken effect.
4037 : : */
1723 heikki.linnakangas@i 4038 [ + + ]:CBC 15561 : if (((volatile XLogRecoveryCtlData *) XLogRecoveryCtl)->recoveryPauseState !=
4039 : : RECOVERY_NOT_PAUSED)
4040 : 7 : recoveryPausesHere(false);
4041 : :
4042 : : /*
4043 : : * This possibly-long loop needs to handle interrupts of startup
4044 : : * process.
4045 : : */
610 4046 : 15561 : ProcessStartupProcInterrupts();
4047 : : }
4048 : :
4049 : : return XLREAD_FAIL; /* not reached */
4050 : : }
4051 : :
4052 : :
4053 : : /*
4054 : : * Determine what log level should be used to report a corrupt WAL record
4055 : : * in the current WAL page, previously read by XLogPageRead().
4056 : : *
4057 : : * 'emode' is the error mode that would be used to report a file-not-found
4058 : : * or legitimate end-of-WAL situation. Generally, we use it as-is, but if
4059 : : * we're retrying the exact same record that we've tried previously, only
4060 : : * complain the first time to keep the noise down. However, we only do when
4061 : : * reading from pg_wal, because we don't expect any invalid records in archive
4062 : : * or in records streamed from the primary. Files in the archive should be complete,
4063 : : * and we should never hit the end of WAL because we stop and wait for more WAL
4064 : : * to arrive before replaying it.
4065 : : *
4066 : : * NOTE: This function remembers the RecPtr value it was last called with,
4067 : : * to suppress repeated messages about the same record. Only call this when
4068 : : * you are about to ereport(), or you might cause a later message to be
4069 : : * erroneously suppressed.
4070 : : */
4071 : : static int
1723 4072 : 325 : emode_for_corrupt_record(int emode, XLogRecPtr RecPtr)
4073 : : {
4074 : : static XLogRecPtr lastComplaint = InvalidXLogRecPtr;
4075 : :
4076 [ + + + - ]: 325 : if (readSource == XLOG_FROM_PG_WAL && emode == LOG)
4077 : : {
4078 [ + + ]: 322 : if (RecPtr == lastComplaint)
4079 : 88 : emode = DEBUG1;
4080 : : else
4081 : 234 : lastComplaint = RecPtr;
4082 : : }
4083 : 325 : return emode;
4084 : : }
4085 : :
4086 : :
4087 : : /*
4088 : : * Subroutine to try to fetch and validate a prior checkpoint record.
4089 : : */
4090 : : static XLogRecord *
1673 tmunro@postgresql.or 4091 : 1131 : ReadCheckpointRecord(XLogPrefetcher *xlogprefetcher, XLogRecPtr RecPtr,
4092 : : TimeLineID replayTLI)
4093 : : {
4094 : : XLogRecord *record;
4095 : : uint8 info;
4096 : :
1723 heikki.linnakangas@i 4097 [ - + ]: 1131 : Assert(xlogreader != NULL);
4098 : :
4099 [ - + ]: 1131 : if (!XRecOffIsValid(RecPtr))
4100 : : {
1569 fujii@postgresql.org 4101 [ # # ]:UBC 0 : ereport(LOG,
4102 : : (errmsg("invalid checkpoint location")));
1723 heikki.linnakangas@i 4103 : 0 : return NULL;
4104 : : }
4105 : :
1673 tmunro@postgresql.or 4106 :CBC 1131 : XLogPrefetcherBeginRead(xlogprefetcher, RecPtr);
4107 : 1131 : record = ReadRecord(xlogprefetcher, LOG, true, replayTLI);
4108 : :
1723 heikki.linnakangas@i 4109 [ + + ]: 1131 : if (record == NULL)
4110 : : {
1569 fujii@postgresql.org 4111 [ + - ]: 2 : ereport(LOG,
4112 : : (errmsg("invalid checkpoint record")));
1723 heikki.linnakangas@i 4113 : 2 : return NULL;
4114 : : }
4115 [ - + ]: 1129 : if (record->xl_rmid != RM_XLOG_ID)
4116 : : {
1569 fujii@postgresql.org 4117 [ # # ]:UBC 0 : ereport(LOG,
4118 : : (errmsg("invalid resource manager ID in checkpoint record")));
1723 heikki.linnakangas@i 4119 : 0 : return NULL;
4120 : : }
1723 heikki.linnakangas@i 4121 :CBC 1129 : info = record->xl_info & ~XLR_INFO_MASK;
4122 [ + + - + ]: 1129 : if (info != XLOG_CHECKPOINT_SHUTDOWN &&
4123 : : info != XLOG_CHECKPOINT_ONLINE)
4124 : : {
1569 fujii@postgresql.org 4125 [ # # ]:UBC 0 : ereport(LOG,
4126 : : (errmsg("invalid xl_info in checkpoint record")));
1723 heikki.linnakangas@i 4127 : 0 : return NULL;
4128 : : }
1723 heikki.linnakangas@i 4129 [ - + ]:CBC 1129 : if (record->xl_tot_len != SizeOfXLogRecord + SizeOfXLogRecordDataHeaderShort + sizeof(CheckPoint))
4130 : : {
1569 fujii@postgresql.org 4131 [ # # ]:UBC 0 : ereport(LOG,
4132 : : (errmsg("invalid length of checkpoint record")));
1723 heikki.linnakangas@i 4133 : 0 : return NULL;
4134 : : }
1723 heikki.linnakangas@i 4135 :CBC 1129 : return record;
4136 : : }
4137 : :
4138 : : /*
4139 : : * Scan for new timelines that might have appeared in the archive since we
4140 : : * started recovery.
4141 : : *
4142 : : * If there are any, the function changes recovery target TLI to the latest
4143 : : * one and returns 'true'.
4144 : : */
4145 : : static bool
4146 : 228 : rescanLatestTimeLine(TimeLineID replayTLI, XLogRecPtr replayLSN)
4147 : : {
4148 : : List *newExpectedTLEs;
4149 : : bool found;
4150 : : ListCell *cell;
4151 : : TimeLineID newtarget;
4152 : 228 : TimeLineID oldtarget = recoveryTargetTLI;
4153 : 228 : TimeLineHistoryEntry *currentTle = NULL;
4154 : :
4155 : 228 : newtarget = findNewestTimeLine(recoveryTargetTLI);
4156 [ + + ]: 228 : if (newtarget == recoveryTargetTLI)
4157 : : {
4158 : : /* No new timelines found */
4159 : 219 : return false;
4160 : : }
4161 : :
4162 : : /*
4163 : : * Determine the list of expected TLIs for the new TLI
4164 : : */
4165 : :
4166 : 9 : newExpectedTLEs = readTimeLineHistory(newtarget);
4167 : :
4168 : : /*
4169 : : * If the current timeline is not part of the history of the new timeline,
4170 : : * we cannot proceed to it.
4171 : : */
4172 : 9 : found = false;
4173 [ + - + - : 18 : foreach(cell, newExpectedTLEs)
+ - ]
4174 : : {
4175 : 18 : currentTle = (TimeLineHistoryEntry *) lfirst(cell);
4176 : :
4177 [ + + ]: 18 : if (currentTle->tli == recoveryTargetTLI)
4178 : : {
4179 : 9 : found = true;
4180 : 9 : break;
4181 : : }
4182 : : }
4183 [ - + ]: 9 : if (!found)
4184 : : {
1723 heikki.linnakangas@i 4185 [ # # ]:UBC 0 : ereport(LOG,
4186 : : (errmsg("new timeline %u is not a child of database system timeline %u",
4187 : : newtarget,
4188 : : replayTLI)));
4189 : 0 : return false;
4190 : : }
4191 : :
4192 : : /*
4193 : : * The current timeline was found in the history file, but check that the
4194 : : * next timeline was forked off from it *after* the current recovery
4195 : : * location.
4196 : : */
1723 heikki.linnakangas@i 4197 [ - + ]:CBC 9 : if (currentTle->end < replayLSN)
4198 : : {
1723 heikki.linnakangas@i 4199 [ # # ]:UBC 0 : ereport(LOG,
4200 : : errmsg("new timeline %u forked off current database system timeline %u before current recovery point %X/%08X",
4201 : : newtarget,
4202 : : replayTLI,
4203 : : LSN_FORMAT_ARGS(replayLSN)));
4204 : 0 : return false;
4205 : : }
4206 : :
4207 : : /* The new timeline history seems valid. Switch target */
1723 heikki.linnakangas@i 4208 :CBC 9 : recoveryTargetTLI = newtarget;
4209 : 9 : list_free_deep(expectedTLEs);
4210 : 9 : expectedTLEs = newExpectedTLEs;
4211 : :
4212 : : /*
4213 : : * As in StartupXLOG(), try to ensure we have all the history files
4214 : : * between the old target and new target in pg_wal.
4215 : : */
4216 : 9 : restoreTimeLineHistoryFiles(oldtarget + 1, newtarget);
4217 : :
4218 [ + - ]: 9 : ereport(LOG,
4219 : : (errmsg("new target timeline is %u",
4220 : : recoveryTargetTLI)));
4221 : :
4222 : 9 : return true;
4223 : : }
4224 : :
4225 : :
4226 : : /*
4227 : : * Open a logfile segment for reading (during recovery).
4228 : : *
4229 : : * If source == XLOG_FROM_ARCHIVE, the segment is retrieved from archive.
4230 : : * Otherwise, it's assumed to be already available in pg_wal.
4231 : : */
4232 : : static int
786 michael@paquier.xyz 4233 : 3764 : XLogFileRead(XLogSegNo segno, TimeLineID tli,
4234 : : XLogSource source, bool notfoundOk)
4235 : : {
4236 : : char xlogfname[MAXFNAMELEN];
4237 : : char activitymsg[MAXFNAMELEN + 16];
4238 : : char path[MAXPGPATH];
4239 : : int fd;
4240 : :
1723 heikki.linnakangas@i 4241 : 3764 : XLogFileName(xlogfname, tli, segno, wal_segment_size);
4242 : :
4243 [ + + - ]: 3764 : switch (source)
4244 : : {
4245 : 975 : case XLOG_FROM_ARCHIVE:
4246 : : /* Report recovery progress in PS display */
4247 : 975 : snprintf(activitymsg, sizeof(activitymsg), "waiting for %s",
4248 : : xlogfname);
4249 : 975 : set_ps_display(activitymsg);
4250 : :
4251 [ + + ]: 975 : if (!RestoreArchivedFile(path, xlogfname,
4252 : : "RECOVERYXLOG",
4253 : : wal_segment_size,
4254 : : InRedo))
4255 : 610 : return -1;
4256 : 365 : break;
4257 : :
4258 : 2789 : case XLOG_FROM_PG_WAL:
4259 : : case XLOG_FROM_STREAM:
4260 : 2789 : XLogFilePath(path, tli, segno, wal_segment_size);
4261 : 2789 : break;
4262 : :
1723 heikki.linnakangas@i 4263 :UBC 0 : default:
4264 [ # # ]: 0 : elog(ERROR, "invalid XLogFileRead source %d", source);
4265 : : }
4266 : :
4267 : : /*
4268 : : * If the segment was fetched from archival storage, replace the existing
4269 : : * xlog segment (if any) with the archival version.
4270 : : */
1723 heikki.linnakangas@i 4271 [ + + ]:CBC 3154 : if (source == XLOG_FROM_ARCHIVE)
4272 : : {
4273 [ - + ]: 365 : Assert(!IsInstallXLogFileSegmentActive());
4274 : 365 : KeepFileRestoredFromArchive(path, xlogfname);
4275 : :
4276 : : /*
4277 : : * Set path to point at the new file in pg_wal.
4278 : : */
4279 : 365 : snprintf(path, MAXPGPATH, XLOGDIR "/%s", xlogfname);
4280 : : }
4281 : :
4282 : 3154 : fd = BasicOpenFile(path, O_RDONLY | PG_BINARY);
4283 [ + + ]: 3154 : if (fd >= 0)
4284 : : {
4285 : : /* Success! */
4286 : 2915 : curFileTLI = tli;
4287 : :
4288 : : /* Report recovery progress in PS display */
4289 : 2915 : snprintf(activitymsg, sizeof(activitymsg), "recovering %s",
4290 : : xlogfname);
4291 : 2915 : set_ps_display(activitymsg);
4292 : :
4293 : : /* Track source of data in assorted state variables */
4294 : 2915 : readSource = source;
4295 : 2915 : XLogReceiptSource = source;
4296 : : /* In FROM_STREAM case, caller tracks receipt time, not me */
4297 [ + + ]: 2915 : if (source != XLOG_FROM_STREAM)
4298 : 1834 : XLogReceiptTime = GetCurrentTimestamp();
4299 : :
4300 : 2915 : return fd;
4301 : : }
4302 [ + - - + ]: 239 : if (errno != ENOENT || !notfoundOk) /* unexpected failure? */
1723 heikki.linnakangas@i 4303 [ # # ]:UBC 0 : ereport(PANIC,
4304 : : (errcode_for_file_access(),
4305 : : errmsg("could not open file \"%s\": %m", path)));
1723 heikki.linnakangas@i 4306 :CBC 239 : return -1;
4307 : : }
4308 : :
4309 : : /*
4310 : : * Open a logfile segment for reading (during recovery).
4311 : : *
4312 : : * This version searches for the segment with any TLI listed in expectedTLEs.
4313 : : */
4314 : : static int
786 michael@paquier.xyz 4315 : 2062 : XLogFileReadAnyTLI(XLogSegNo segno, XLogSource source)
4316 : : {
4317 : : char path[MAXPGPATH];
4318 : : ListCell *cell;
4319 : : int fd;
4320 : : List *tles;
4321 : :
4322 : : /*
4323 : : * Loop looking for a suitable timeline ID: we might need to read any of
4324 : : * the timelines listed in expectedTLEs.
4325 : : *
4326 : : * We expect curFileTLI on entry to be the TLI of the preceding file in
4327 : : * sequence, or 0 if there was no predecessor. We do not allow curFileTLI
4328 : : * to go backwards; this prevents us from picking up the wrong file when a
4329 : : * parent timeline extends to higher segment numbers than the child we
4330 : : * want to read.
4331 : : *
4332 : : * If we haven't read the timeline history file yet, read it now, so that
4333 : : * we know which TLIs to scan. We don't save the list in expectedTLEs,
4334 : : * however, unless we actually find a valid segment. That way if there is
4335 : : * neither a timeline history file nor a WAL segment in the archive, and
4336 : : * streaming replication is set up, we'll read the timeline history file
4337 : : * streamed from the primary when we start streaming, instead of
4338 : : * recovering with a dummy history generated here.
4339 : : */
1723 heikki.linnakangas@i 4340 [ + + ]: 2062 : if (expectedTLEs)
4341 : 931 : tles = expectedTLEs;
4342 : : else
4343 : 1131 : tles = readTimeLineHistory(recoveryTargetTLI);
4344 : :
4345 [ + - + + : 2307 : foreach(cell, tles)
+ + ]
4346 : : {
4347 : 2087 : TimeLineHistoryEntry *hent = (TimeLineHistoryEntry *) lfirst(cell);
4348 : 2087 : TimeLineID tli = hent->tli;
4349 : :
4350 [ + + ]: 2087 : if (tli < curFileTLI)
4351 : 8 : break; /* don't bother looking at too-old TLIs */
4352 : :
4353 : : /*
4354 : : * Skip scanning the timeline ID that the logfile segment to read
4355 : : * doesn't belong to
4356 : : */
364 alvherre@kurilemu.de 4357 [ + + ]: 2079 : if (XLogRecPtrIsValid(hent->begin))
4358 : : {
1723 heikki.linnakangas@i 4359 : 89 : XLogSegNo beginseg = 0;
4360 : :
4361 : 89 : XLByteToSeg(hent->begin, beginseg, wal_segment_size);
4362 : :
4363 : : /*
4364 : : * The logfile segment that doesn't belong to the timeline is
4365 : : * older or newer than the segment that the timeline started or
4366 : : * ended at, respectively. It's sufficient to check only the
4367 : : * starting segment of the timeline here. Since the timelines are
4368 : : * scanned in descending order in this loop, any segments newer
4369 : : * than the ending segment should belong to newer timeline and
4370 : : * have already been read before. So it's not necessary to check
4371 : : * the ending segment of the timeline here.
4372 : : */
4373 [ + + ]: 89 : if (segno < beginseg)
4374 : 6 : continue;
4375 : : }
4376 : :
4377 [ + + - + ]: 2073 : if (source == XLOG_FROM_ANY || source == XLOG_FROM_ARCHIVE)
4378 : : {
786 michael@paquier.xyz 4379 : 975 : fd = XLogFileRead(segno, tli, XLOG_FROM_ARCHIVE, true);
1723 heikki.linnakangas@i 4380 [ + + ]: 975 : if (fd != -1)
4381 : : {
4382 [ - + ]: 365 : elog(DEBUG1, "got WAL segment from archive");
4383 [ + + ]: 365 : if (!expectedTLEs)
4384 : 20 : expectedTLEs = tles;
4385 : 1834 : return fd;
4386 : : }
4387 : : }
4388 : :
4389 [ + + + - ]: 1708 : if (source == XLOG_FROM_ANY || source == XLOG_FROM_PG_WAL)
4390 : : {
786 michael@paquier.xyz 4391 : 1708 : fd = XLogFileRead(segno, tli, XLOG_FROM_PG_WAL, true);
1723 heikki.linnakangas@i 4392 [ + + ]: 1708 : if (fd != -1)
4393 : : {
4394 [ + + ]: 1469 : if (!expectedTLEs)
4395 : 1109 : expectedTLEs = tles;
4396 : 1469 : return fd;
4397 : : }
4398 : : }
4399 : : }
4400 : :
4401 : : /* Couldn't find it. For simplicity, complain about front timeline */
4402 : 228 : XLogFilePath(path, recoveryTargetTLI, segno, wal_segment_size);
4403 : 228 : errno = ENOENT;
786 michael@paquier.xyz 4404 [ + + ]: 228 : ereport(DEBUG2,
4405 : : (errcode_for_file_access(),
4406 : : errmsg("could not open file \"%s\": %m", path)));
1723 heikki.linnakangas@i 4407 : 228 : return -1;
4408 : : }
4409 : :
4410 : : /*
4411 : : * Set flag to signal the walreceiver to restart. (The startup process calls
4412 : : * this on noticing a relevant configuration change.)
4413 : : */
4414 : : void
4415 : 15 : StartupRequestWalReceiverRestart(void)
4416 : : {
4417 [ + - + + ]: 15 : if (currentSource == XLOG_FROM_STREAM && WalRcvRunning())
4418 : : {
4419 [ + - ]: 9 : ereport(LOG,
4420 : : (errmsg("WAL receiver process shutdown requested")));
4421 : :
4422 : 9 : pendingWalRcvRestart = true;
4423 : : }
4424 : 15 : }
4425 : :
4426 : :
4427 : : /*
4428 : : * Has a standby promotion already been triggered?
4429 : : *
4430 : : * Unlike CheckForStandbyTrigger(), this works in any process
4431 : : * that's connected to shared memory.
4432 : : */
4433 : : bool
4434 : 91 : PromoteIsTriggered(void)
4435 : : {
4436 : : /*
4437 : : * We check shared state each time only until a standby promotion is
4438 : : * triggered. We can't trigger a promotion again, so there's no need to
4439 : : * keep checking after the shared variable has once been seen true.
4440 : : */
4441 [ + + ]: 91 : if (LocalPromoteIsTriggered)
4442 : 62 : return true;
4443 : :
4444 : 29 : SpinLockAcquire(&XLogRecoveryCtl->info_lck);
4445 : 29 : LocalPromoteIsTriggered = XLogRecoveryCtl->SharedPromoteIsTriggered;
4446 : 29 : SpinLockRelease(&XLogRecoveryCtl->info_lck);
4447 : :
4448 : 29 : return LocalPromoteIsTriggered;
4449 : : }
4450 : :
4451 : : static void
4452 : 59 : SetPromoteIsTriggered(void)
4453 : : {
4454 : 59 : SpinLockAcquire(&XLogRecoveryCtl->info_lck);
4455 : 59 : XLogRecoveryCtl->SharedPromoteIsTriggered = true;
4456 : 59 : SpinLockRelease(&XLogRecoveryCtl->info_lck);
4457 : :
4458 : : /*
4459 : : * Mark the recovery pause state as 'not paused' because the paused state
4460 : : * ends and promotion continues if a promotion is triggered while recovery
4461 : : * is paused. Otherwise pg_get_wal_replay_pause_state() can mistakenly
4462 : : * return 'paused' while a promotion is ongoing.
4463 : : */
4464 : 59 : SetRecoveryPause(false);
4465 : :
4466 : 59 : LocalPromoteIsTriggered = true;
4467 : 59 : }
4468 : :
4469 : : /*
4470 : : * Check whether a promote request has arrived.
4471 : : */
4472 : : static bool
4473 : 14672 : CheckForStandbyTrigger(void)
4474 : : {
4475 [ + + ]: 14672 : if (LocalPromoteIsTriggered)
4476 : 57 : return true;
4477 : :
4478 [ + + + - ]: 14615 : if (IsPromoteSignaled() && CheckPromoteSignal())
4479 : : {
4480 [ + - ]: 59 : ereport(LOG, (errmsg("received promote request")));
4481 : 59 : RemovePromoteSignalFiles();
4482 : 59 : ResetPromoteSignaled();
4483 : 59 : SetPromoteIsTriggered();
4484 : 59 : return true;
4485 : : }
4486 : :
4487 : 14556 : return false;
4488 : : }
4489 : :
4490 : : /*
4491 : : * Remove the files signaling a standby promotion request.
4492 : : */
4493 : : void
4494 : 1099 : RemovePromoteSignalFiles(void)
4495 : : {
4496 : 1099 : unlink(PROMOTE_SIGNAL_FILE);
4497 : 1099 : }
4498 : :
4499 : : /*
4500 : : * Check to see if a promote request has arrived.
4501 : : */
4502 : : bool
4503 : 822 : CheckPromoteSignal(void)
4504 : : {
4505 : : struct stat stat_buf;
4506 : :
4507 [ + + ]: 822 : if (stat(PROMOTE_SIGNAL_FILE, &stat_buf) == 0)
4508 : 118 : return true;
4509 : :
4510 : 704 : return false;
4511 : : }
4512 : :
4513 : : /*
4514 : : * Has hot standby initialization started pg_subtrans?
4515 : : */
4516 : : bool
64 fujii@postgresql.org 4517 : 216 : RecoverySubtransInitialized(void)
4518 : : {
4519 : : bool result;
4520 : :
4521 : 216 : SpinLockAcquire(&XLogRecoveryCtl->info_lck);
4522 : 216 : result = XLogRecoveryCtl->SharedRecoverySubtransInitialized;
4523 : 216 : SpinLockRelease(&XLogRecoveryCtl->info_lck);
4524 : :
4525 : 216 : return result;
4526 : : }
4527 : :
4528 : : /*
4529 : : * Remember that hot standby initialization has started pg_subtrans.
4530 : : */
4531 : : void
4532 : 137 : SetRecoverySubtransInitialized(void)
4533 : : {
4534 : 137 : SpinLockAcquire(&XLogRecoveryCtl->info_lck);
4535 : 137 : XLogRecoveryCtl->SharedRecoverySubtransInitialized = true;
4536 : 137 : SpinLockRelease(&XLogRecoveryCtl->info_lck);
4537 : 137 : }
4538 : :
4539 : : /*
4540 : : * Wake up startup process to replay newly arrived WAL, or to notice that
4541 : : * failover has been requested.
4542 : : */
4543 : : void
1723 heikki.linnakangas@i 4544 : 55771 : WakeupRecovery(void)
4545 : : {
4546 : 55771 : SetLatch(&XLogRecoveryCtl->recoveryWakeupLatch);
4547 : 55771 : }
4548 : :
4549 : : /*
4550 : : * Schedule a walreceiver wakeup in the main recovery loop.
4551 : : */
4552 : : void
4553 : 3 : XLogRequestWalReceiverReply(void)
4554 : : {
4555 : 3 : doRequestWalReceiverReply = true;
4556 : 3 : }
4557 : :
4558 : : /*
4559 : : * Is HotStandby active yet? This is only important in special backends
4560 : : * since normal backends won't ever be able to connect until this returns
4561 : : * true. Postmaster knows this by way of signal, not via shared memory.
4562 : : *
4563 : : * Unlike testing standbyState, this works in any process that's connected to
4564 : : * shared memory. (And note that standbyState alone doesn't tell the truth
4565 : : * anyway.)
4566 : : */
4567 : : bool
4568 : 197 : HotStandbyActive(void)
4569 : : {
4570 : : /*
4571 : : * We check shared state each time only until Hot Standby is active. We
4572 : : * can't de-activate Hot Standby, so there's no need to keep checking
4573 : : * after the shared variable has once been seen true.
4574 : : */
4575 [ + + ]: 197 : if (LocalHotStandbyActive)
4576 : 26 : return true;
4577 : : else
4578 : : {
4579 : : /* spinlock is essential on machines with weak memory ordering! */
4580 : 171 : SpinLockAcquire(&XLogRecoveryCtl->info_lck);
4581 : 171 : LocalHotStandbyActive = XLogRecoveryCtl->SharedHotStandbyActive;
4582 : 171 : SpinLockRelease(&XLogRecoveryCtl->info_lck);
4583 : :
4584 : 171 : return LocalHotStandbyActive;
4585 : : }
4586 : : }
4587 : :
4588 : : /*
4589 : : * Like HotStandbyActive(), but to be used only in WAL replay code,
4590 : : * where we don't need to ask any other process what the state is.
4591 : : */
4592 : : static bool
1723 heikki.linnakangas@i 4593 :UBC 0 : HotStandbyActiveInReplay(void)
4594 : : {
4595 [ # # # # ]: 0 : Assert(AmStartupProcess() || !IsPostmasterEnvironment);
4596 : 0 : return LocalHotStandbyActive;
4597 : : }
4598 : :
4599 : : /*
4600 : : * Get latest redo apply position.
4601 : : *
4602 : : * Exported to allow WALReceiver to read the pointer directly.
4603 : : */
4604 : : XLogRecPtr
1723 heikki.linnakangas@i 4605 :CBC 135896 : GetXLogReplayRecPtr(TimeLineID *replayTLI)
4606 : : {
4607 : : XLogRecPtr recptr;
4608 : : TimeLineID tli;
4609 : :
4610 : 135896 : SpinLockAcquire(&XLogRecoveryCtl->info_lck);
4611 : 135896 : recptr = XLogRecoveryCtl->lastReplayedEndRecPtr;
4612 : 135896 : tli = XLogRecoveryCtl->lastReplayedTLI;
4613 : 135896 : SpinLockRelease(&XLogRecoveryCtl->info_lck);
4614 : :
4615 [ + + ]: 135896 : if (replayTLI)
4616 : 3357 : *replayTLI = tli;
4617 : 135896 : return recptr;
4618 : : }
4619 : :
4620 : :
4621 : : /*
4622 : : * Get position of last applied, or the record being applied.
4623 : : *
4624 : : * This is different from GetXLogReplayRecPtr() in that if a WAL
4625 : : * record is currently being applied, this includes that record.
4626 : : */
4627 : : XLogRecPtr
4628 : 7024 : GetCurrentReplayRecPtr(TimeLineID *replayEndTLI)
4629 : : {
4630 : : XLogRecPtr recptr;
4631 : : TimeLineID tli;
4632 : :
4633 : 7024 : SpinLockAcquire(&XLogRecoveryCtl->info_lck);
4634 : 7024 : recptr = XLogRecoveryCtl->replayEndRecPtr;
4635 : 7024 : tli = XLogRecoveryCtl->replayEndTLI;
4636 : 7024 : SpinLockRelease(&XLogRecoveryCtl->info_lck);
4637 : :
4638 [ + - ]: 7024 : if (replayEndTLI)
4639 : 7024 : *replayEndTLI = tli;
4640 : 7024 : return recptr;
4641 : : }
4642 : :
4643 : : /*
4644 : : * Save timestamp of latest processed commit/abort record.
4645 : : *
4646 : : * We keep this in XLogRecoveryCtl, not a simple static variable, so that it can be
4647 : : * seen by processes other than the startup process. Note in particular
4648 : : * that CreateRestartPoint is executed in the checkpointer.
4649 : : */
4650 : : static void
4651 : 23440 : SetLatestXTime(TimestampTz xtime)
4652 : : {
4653 : 23440 : SpinLockAcquire(&XLogRecoveryCtl->info_lck);
4654 : 23440 : XLogRecoveryCtl->recoveryLastXTime = xtime;
4655 : 23440 : SpinLockRelease(&XLogRecoveryCtl->info_lck);
4656 : 23440 : }
4657 : :
4658 : : /*
4659 : : * Fetch timestamp of latest processed commit/abort record.
4660 : : */
4661 : : TimestampTz
4662 : 386 : GetLatestXTime(void)
4663 : : {
4664 : : TimestampTz xtime;
4665 : :
4666 : 386 : SpinLockAcquire(&XLogRecoveryCtl->info_lck);
4667 : 386 : xtime = XLogRecoveryCtl->recoveryLastXTime;
4668 : 386 : SpinLockRelease(&XLogRecoveryCtl->info_lck);
4669 : :
4670 : 386 : return xtime;
4671 : : }
4672 : :
4673 : : /*
4674 : : * Save timestamp of the next chunk of WAL records to apply.
4675 : : *
4676 : : * We keep this in XLogRecoveryCtl, not a simple static variable, so that it can be
4677 : : * seen by all backends.
4678 : : */
4679 : : static void
4680 : 11018 : SetCurrentChunkStartTime(TimestampTz xtime)
4681 : : {
4682 : 11018 : SpinLockAcquire(&XLogRecoveryCtl->info_lck);
4683 : 11018 : XLogRecoveryCtl->currentChunkStartTime = xtime;
4684 : 11018 : SpinLockRelease(&XLogRecoveryCtl->info_lck);
4685 : 11018 : }
4686 : :
4687 : : /*
4688 : : * Fetch timestamp of latest processed commit/abort record.
4689 : : * Startup process maintains an accurate local copy in XLogReceiptTime
4690 : : */
4691 : : TimestampTz
4692 : 279 : GetCurrentChunkReplayStartTime(void)
4693 : : {
4694 : : TimestampTz xtime;
4695 : :
4696 : 279 : SpinLockAcquire(&XLogRecoveryCtl->info_lck);
4697 : 279 : xtime = XLogRecoveryCtl->currentChunkStartTime;
4698 : 279 : SpinLockRelease(&XLogRecoveryCtl->info_lck);
4699 : :
4700 : 279 : return xtime;
4701 : : }
4702 : :
4703 : : /*
4704 : : * Returns time of receipt of current chunk of XLOG data, as well as
4705 : : * whether it was received from streaming replication or from archives.
4706 : : */
4707 : : void
4708 : 50 : GetXLogReceiptTime(TimestampTz *rtime, bool *fromStream)
4709 : : {
4710 : : /*
4711 : : * This must be executed in the startup process, since we don't export the
4712 : : * relevant state to shared memory.
4713 : : */
4714 [ - + ]: 50 : Assert(InRecovery);
4715 : :
4716 : 50 : *rtime = XLogReceiptTime;
4717 : 50 : *fromStream = (XLogReceiptSource == XLOG_FROM_STREAM);
4718 : 50 : }
4719 : :
4720 : : /*
4721 : : * Note that text field supplied is a parameter name and does not require
4722 : : * translation
4723 : : */
4724 : : void
4725 : 805 : RecoveryRequiresIntParameter(const char *param_name, int currValue, int minValue)
4726 : : {
4727 [ - + ]: 805 : if (currValue < minValue)
4728 : : {
1723 heikki.linnakangas@i 4729 [ # # ]:UBC 0 : if (HotStandbyActiveInReplay())
4730 : : {
4731 : 0 : bool warned_for_promote = false;
4732 : :
4733 [ # # ]: 0 : ereport(WARNING,
4734 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
4735 : : errmsg("hot standby is not possible because of insufficient parameter settings"),
4736 : : errdetail("%s = %d is a lower setting than on the primary server, where its value was %d.",
4737 : : param_name,
4738 : : currValue,
4739 : : minValue)));
4740 : :
4741 : 0 : SetRecoveryPause(true);
4742 : :
4743 [ # # ]: 0 : ereport(LOG,
4744 : : (errmsg("recovery has paused"),
4745 : : errdetail("If recovery is unpaused, the server will shut down."),
4746 : : errhint("You can then restart the server after making the necessary configuration changes.")));
4747 : :
4748 [ # # ]: 0 : while (GetRecoveryPauseState() != RECOVERY_NOT_PAUSED)
4749 : : {
610 4750 : 0 : ProcessStartupProcInterrupts();
4751 : :
1723 4752 [ # # ]: 0 : if (CheckForStandbyTrigger())
4753 : : {
4754 [ # # ]: 0 : if (!warned_for_promote)
4755 [ # # ]: 0 : ereport(WARNING,
4756 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
4757 : : errmsg("promotion is not possible because of insufficient parameter settings"),
4758 : :
4759 : : /*
4760 : : * Repeat the detail from above so it's easy to find
4761 : : * in the log.
4762 : : */
4763 : : errdetail("%s = %d is a lower setting than on the primary server, where its value was %d.",
4764 : : param_name,
4765 : : currValue,
4766 : : minValue),
4767 : : errhint("Restart the server after making the necessary configuration changes.")));
4768 : 0 : warned_for_promote = true;
4769 : : }
4770 : :
4771 : : /*
4772 : : * If recovery pause is requested then set it paused. While
4773 : : * we are in the loop, user might resume and pause again so
4774 : : * set this every time.
4775 : : */
4776 : 0 : ConfirmRecoveryPaused();
4777 : :
4778 : : /*
4779 : : * We wait on a condition variable that will wake us as soon
4780 : : * as the pause ends, but we use a timeout so we can check the
4781 : : * above conditions periodically too.
4782 : : */
4783 : 0 : ConditionVariableTimedSleep(&XLogRecoveryCtl->recoveryNotPausedCV, 1000,
4784 : : WAIT_EVENT_RECOVERY_PAUSE);
4785 : : }
4786 : 0 : ConditionVariableCancelSleep();
4787 : : }
4788 : :
4789 [ # # ]: 0 : ereport(FATAL,
4790 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
4791 : : errmsg("recovery aborted because of insufficient parameter settings"),
4792 : : /* Repeat the detail from above so it's easy to find in the log. */
4793 : : errdetail("%s = %d is a lower setting than on the primary server, where its value was %d.",
4794 : : param_name,
4795 : : currValue,
4796 : : minValue),
4797 : : errhint("You can restart the server after making the necessary configuration changes.")));
4798 : : }
1723 heikki.linnakangas@i 4799 :CBC 805 : }
4800 : :
4801 : :
4802 : : /*
4803 : : * GUC check_hook for primary_slot_name
4804 : : */
4805 : : bool
1514 tgl@sss.pgh.pa.us 4806 : 1540 : check_primary_slot_name(char **newval, void **extra, GucSource source)
4807 : : {
4808 : : int err_code;
379 fujii@postgresql.org 4809 : 1540 : char *err_msg = NULL;
4810 : 1540 : char *err_hint = NULL;
4811 : :
1514 tgl@sss.pgh.pa.us 4812 [ + - + + ]: 1540 : if (*newval && strcmp(*newval, "") != 0 &&
379 fujii@postgresql.org 4813 [ - + ]: 190 : !ReplicationSlotValidateNameInternal(*newval, false, &err_code,
4814 : : &err_msg, &err_hint))
4815 : : {
379 fujii@postgresql.org 4816 :UBC 0 : GUC_check_errcode(err_code);
4817 : 0 : GUC_check_errdetail("%s", err_msg);
4818 [ # # ]: 0 : if (err_hint != NULL)
4819 : 0 : GUC_check_errhint("%s", err_hint);
1514 tgl@sss.pgh.pa.us 4820 : 0 : return false;
4821 : : }
4822 : :
1514 tgl@sss.pgh.pa.us 4823 :CBC 1540 : return true;
4824 : : }
4825 : :
4826 : : /*
4827 : : * Return the recovery target derived from the recovery_target* settings,
4828 : : * raising an error if more than one of them is set.
4829 : : */
4830 : : static RecoveryTargetType
108 fujii@postgresql.org 4831 :GNC 1134 : DetermineRecoveryTargetType(void)
4832 : : {
4833 : 1134 : int ntargets = 0;
4834 : 1134 : RecoveryTargetType target = RECOVERY_TARGET_UNSET;
4835 : : const char *val;
4836 : : StringInfoData buf;
4837 : :
4838 : 1134 : initStringInfo(&buf);
4839 : :
4840 : : #define ADD_TARGET_IF_SET(gucname, kind) \
4841 : : do { \
4842 : : val = GetConfigOption(gucname, false, false); \
4843 : : if (val[0] != '\0') \
4844 : : { \
4845 : : ntargets++; \
4846 : : target = (kind); \
4847 : : if (buf.len == 0) \
4848 : : appendStringInfo(&buf, _("\"%s\""), gucname); \
4849 : : else \
4850 : : appendStringInfo(&buf, _(", \"%s\""), gucname); \
4851 : : } \
4852 : : } while (0)
4853 : :
4854 [ + + + - ]: 1134 : ADD_TARGET_IF_SET("recovery_target", RECOVERY_TARGET_IMMEDIATE);
4855 [ + + + - ]: 1134 : ADD_TARGET_IF_SET("recovery_target_lsn", RECOVERY_TARGET_LSN);
4856 [ + + + - ]: 1134 : ADD_TARGET_IF_SET("recovery_target_name", RECOVERY_TARGET_NAME);
4857 [ + + - + ]: 1134 : ADD_TARGET_IF_SET("recovery_target_time", RECOVERY_TARGET_TIME);
4858 [ + + - + ]: 1134 : ADD_TARGET_IF_SET("recovery_target_xid", RECOVERY_TARGET_XID);
4859 : : #undef ADD_TARGET_IF_SET
4860 : :
4861 [ + + ]: 1134 : if (ntargets > 1)
4862 [ + - ]: 2 : ereport(FATAL,
4863 : : errcode(ERRCODE_INVALID_PARAMETER_VALUE),
4864 : : errmsg("cannot specify more than one recovery target"),
4865 : : errdetail("Parameters set are: %s.",
4866 : : buf.data));
4867 : :
4868 : 1132 : pfree(buf.data);
4869 : :
4870 : 1132 : return target;
4871 : : }
4872 : :
4873 : : /*
4874 : : * GUC check_hook for recovery_target
4875 : : */
4876 : : bool
1514 tgl@sss.pgh.pa.us 4877 :CBC 1351 : check_recovery_target(char **newval, void **extra, GucSource source)
4878 : : {
4879 [ + + - + ]: 1351 : if (strcmp(*newval, "immediate") != 0 && strcmp(*newval, "") != 0)
4880 : : {
1514 tgl@sss.pgh.pa.us 4881 :UBC 0 : GUC_check_errdetail("The only allowed value is \"immediate\".");
4882 : 0 : return false;
4883 : : }
1514 tgl@sss.pgh.pa.us 4884 :CBC 1351 : return true;
4885 : : }
4886 : :
4887 : : /*
4888 : : * GUC check_hook for recovery_target_lsn
4889 : : */
4890 : : bool
4891 : 1356 : check_recovery_target_lsn(char **newval, void **extra, GucSource source)
4892 : : {
4893 [ + + ]: 1356 : if (strcmp(*newval, "") != 0)
4894 : : {
4895 : : XLogRecPtr lsn;
4896 : : XLogRecPtr *myextra;
426 michael@paquier.xyz 4897 : 8 : ErrorSaveContext escontext = {T_ErrorSaveContext};
4898 : :
4899 : 8 : lsn = pg_lsn_in_safe(*newval, (Node *) &escontext);
4900 [ - + ]: 8 : if (escontext.error_occurred)
1514 tgl@sss.pgh.pa.us 4901 :UBC 0 : return false;
4902 : :
588 dgustafsson@postgres 4903 :CBC 8 : myextra = (XLogRecPtr *) guc_malloc(LOG, sizeof(XLogRecPtr));
4904 [ - + ]: 8 : if (!myextra)
588 dgustafsson@postgres 4905 :UBC 0 : return false;
1514 tgl@sss.pgh.pa.us 4906 :CBC 8 : *myextra = lsn;
707 peter@eisentraut.org 4907 : 8 : *extra = myextra;
4908 : : }
1514 tgl@sss.pgh.pa.us 4909 : 1356 : return true;
4910 : : }
4911 : :
4912 : : /*
4913 : : * GUC assign_hook for recovery_target_lsn
4914 : : */
4915 : : void
4916 : 1356 : assign_recovery_target_lsn(const char *newval, void *extra)
4917 : : {
4918 [ + - + + ]: 1356 : if (newval && strcmp(newval, "") != 0)
4919 : 8 : recoveryTargetLSN = *((XLogRecPtr *) extra);
4920 : 1356 : }
4921 : :
4922 : : /*
4923 : : * GUC check_hook for recovery_target_name
4924 : : */
4925 : : bool
4926 : 1358 : check_recovery_target_name(char **newval, void **extra, GucSource source)
4927 : : {
4928 : : /* Use the value of newval directly */
4929 [ - + ]: 1358 : if (strlen(*newval) >= MAXFNAMELEN)
4930 : : {
902 peter@eisentraut.org 4931 :UBC 0 : GUC_check_errdetail("\"%s\" is too long (maximum %d characters).",
4932 : : "recovery_target_name", MAXFNAMELEN - 1);
1514 tgl@sss.pgh.pa.us 4933 : 0 : return false;
4934 : : }
1514 tgl@sss.pgh.pa.us 4935 :CBC 1358 : return true;
4936 : : }
4937 : :
4938 : : /*
4939 : : * GUC check_hook for recovery_target_time
4940 : : *
4941 : : * The interpretation of the recovery_target_time string can depend on the
4942 : : * time zone setting, so we need to wait until after all GUC processing is
4943 : : * done before we can do the final parsing of the string. This check function
4944 : : * only does a parsing pass to catch syntax errors, but we store the string
4945 : : * and parse it again when we need to use it.
4946 : : */
4947 : : bool
4948 : 1353 : check_recovery_target_time(char **newval, void **extra, GucSource source)
4949 : : {
4950 [ + + ]: 1353 : if (strcmp(*newval, "") != 0)
4951 : : {
4952 : : /* reject some special values */
4953 [ + - ]: 3 : if (strcmp(*newval, "now") == 0 ||
4954 [ + - ]: 3 : strcmp(*newval, "today") == 0 ||
4955 [ + - ]: 3 : strcmp(*newval, "tomorrow") == 0 ||
4956 [ - + ]: 3 : strcmp(*newval, "yesterday") == 0)
4957 : : {
1514 tgl@sss.pgh.pa.us 4958 :UBC 0 : return false;
4959 : : }
4960 : :
4961 : : /*
4962 : : * parse timestamp value (see also timestamptz_in())
4963 : : */
4964 : : {
1514 tgl@sss.pgh.pa.us 4965 :CBC 3 : char *str = *newval;
4966 : : fsec_t fsec;
4967 : : struct pg_tm tt,
4968 : 3 : *tm = &tt;
4969 : : int tz;
4970 : : int dtype;
4971 : : int nf;
4972 : : int dterr;
4973 : : char *field[MAXDATEFIELDS];
4974 : : int ftype[MAXDATEFIELDS];
4975 : : char workbuf[MAXDATELEN + MAXDATEFIELDS];
4976 : : DateTimeErrorExtra dtextra;
4977 : : TimestampTz timestamp;
4978 : :
4979 : 3 : dterr = ParseDateTime(str, workbuf, sizeof(workbuf),
4980 : : field, ftype, MAXDATEFIELDS, &nf);
4981 [ + - ]: 3 : if (dterr == 0)
1427 4982 : 3 : dterr = DecodeDateTime(field, ftype, nf,
4983 : : &dtype, tm, &fsec, &tz, &dtextra);
1514 4984 [ - + ]: 3 : if (dterr != 0)
1514 tgl@sss.pgh.pa.us 4985 :UBC 0 : return false;
1514 tgl@sss.pgh.pa.us 4986 [ - + ]:CBC 3 : if (dtype != DTK_DATE)
1514 tgl@sss.pgh.pa.us 4987 :UBC 0 : return false;
4988 : :
1514 tgl@sss.pgh.pa.us 4989 [ - + ]:CBC 3 : if (tm2timestamp(tm, fsec, &tz, ×tamp) != 0)
4990 : : {
708 alvherre@alvh.no-ip. 4991 :UBC 0 : GUC_check_errdetail("Timestamp out of range: \"%s\".", str);
1514 tgl@sss.pgh.pa.us 4992 : 0 : return false;
4993 : : }
4994 : : }
4995 : : }
1514 tgl@sss.pgh.pa.us 4996 :CBC 1353 : return true;
4997 : : }
4998 : :
4999 : : /*
5000 : : * GUC check_hook for recovery_target_timeline
5001 : : */
5002 : : bool
5003 : 1353 : check_recovery_target_timeline(char **newval, void **extra, GucSource source)
5004 : : {
5005 : : RecoveryTargetTimeLineGoal rttg;
5006 : : RecoveryTargetTimeLineGoal *myextra;
5007 : :
5008 [ - + ]: 1353 : if (strcmp(*newval, "current") == 0)
1514 tgl@sss.pgh.pa.us 5009 :UBC 0 : rttg = RECOVERY_TARGET_TIMELINE_CONTROLFILE;
1514 tgl@sss.pgh.pa.us 5010 [ + + ]:CBC 1353 : else if (strcmp(*newval, "latest") == 0)
5011 : 1350 : rttg = RECOVERY_TARGET_TIMELINE_LATEST;
5012 : : else
5013 : : {
5014 : : char *endp;
5015 : : uint64 timeline;
5016 : :
5017 : 3 : rttg = RECOVERY_TARGET_TIMELINE_NUMERIC;
5018 : :
5019 : 3 : errno = 0;
490 michael@paquier.xyz 5020 : 3 : timeline = strtou64(*newval, &endp, 0);
5021 : :
5022 [ + + + - : 3 : if (*endp != '\0' || errno == EINVAL || errno == ERANGE)
- + ]
5023 : : {
5024 : 1 : GUC_check_errdetail("\"%s\" is not a valid number.",
5025 : : "recovery_target_timeline");
5026 : 3 : return false;
5027 : : }
5028 : :
5029 [ + + + - ]: 2 : if (timeline < 1 || timeline > PG_UINT32_MAX)
5030 : : {
5031 : 2 : GUC_check_errdetail("\"%s\" must be between %u and %u.",
5032 : : "recovery_target_timeline", 1, PG_UINT32_MAX);
1514 tgl@sss.pgh.pa.us 5033 : 2 : return false;
5034 : : }
5035 : : }
5036 : :
588 dgustafsson@postgres 5037 : 1350 : myextra = (RecoveryTargetTimeLineGoal *) guc_malloc(LOG, sizeof(RecoveryTargetTimeLineGoal));
5038 [ - + ]: 1350 : if (!myextra)
588 dgustafsson@postgres 5039 :UBC 0 : return false;
1514 tgl@sss.pgh.pa.us 5040 :CBC 1350 : *myextra = rttg;
707 peter@eisentraut.org 5041 : 1350 : *extra = myextra;
5042 : :
1514 tgl@sss.pgh.pa.us 5043 : 1350 : return true;
5044 : : }
5045 : :
5046 : : /*
5047 : : * GUC assign_hook for recovery_target_timeline
5048 : : */
5049 : : void
5050 : 1350 : assign_recovery_target_timeline(const char *newval, void *extra)
5051 : : {
5052 : 1350 : recoveryTargetTimeLineGoal = *((RecoveryTargetTimeLineGoal *) extra);
5053 [ - + ]: 1350 : if (recoveryTargetTimeLineGoal == RECOVERY_TARGET_TIMELINE_NUMERIC)
1514 tgl@sss.pgh.pa.us 5054 :UBC 0 : recoveryTargetTLIRequested = (TimeLineID) strtoul(newval, NULL, 0);
5055 : : else
1514 tgl@sss.pgh.pa.us 5056 :CBC 1350 : recoveryTargetTLIRequested = 0;
5057 : 1350 : }
5058 : :
5059 : : /*
5060 : : * GUC check_hook for recovery_target_xid
5061 : : */
5062 : : bool
5063 : 1356 : check_recovery_target_xid(char **newval, void **extra, GucSource source)
5064 : : {
5065 [ + + ]: 1356 : if (strcmp(*newval, "") != 0)
5066 : : {
5067 : : TransactionId xid;
5068 : : TransactionId *myextra;
5069 : : char *endp;
5070 : : char *val;
5071 : :
5072 : 6 : errno = 0;
5073 : :
5074 : : /*
5075 : : * Consume leading whitespace to determine if number is negative
5076 : : */
245 fujii@postgresql.org 5077 : 6 : val = *newval;
5078 : :
5079 [ - + ]: 6 : while (isspace((unsigned char) *val))
245 fujii@postgresql.org 5080 :UBC 0 : val++;
5081 : :
5082 : : /*
5083 : : * This cast will remove the epoch, if any
5084 : : */
245 fujii@postgresql.org 5085 :CBC 6 : xid = (TransactionId) strtou64(val, &endp, 0);
5086 : :
5087 [ + + + - : 6 : if (*endp != '\0' || errno == EINVAL || errno == ERANGE || *val == '-')
+ - - + ]
5088 : : {
5089 : 1 : GUC_check_errdetail("\"%s\" is not a valid number.",
5090 : : "recovery_target_xid");
5091 : 1 : return false;
5092 : : }
5093 : :
5094 [ - + ]: 5 : if (xid < FirstNormalTransactionId)
5095 : : {
245 fujii@postgresql.org 5096 :UBC 0 : GUC_check_errdetail("\"%s\" without epoch must be greater than or equal to %u.",
5097 : : "recovery_target_xid",
5098 : : FirstNormalTransactionId);
1514 tgl@sss.pgh.pa.us 5099 : 0 : return false;
5100 : : }
5101 : :
588 dgustafsson@postgres 5102 :CBC 5 : myextra = (TransactionId *) guc_malloc(LOG, sizeof(TransactionId));
5103 [ - + ]: 5 : if (!myextra)
588 dgustafsson@postgres 5104 :UBC 0 : return false;
1514 tgl@sss.pgh.pa.us 5105 :CBC 5 : *myextra = xid;
707 peter@eisentraut.org 5106 : 5 : *extra = myextra;
5107 : : }
1514 tgl@sss.pgh.pa.us 5108 : 1355 : return true;
5109 : : }
5110 : :
5111 : : /*
5112 : : * GUC assign_hook for recovery_target_xid
5113 : : */
5114 : : void
5115 : 1355 : assign_recovery_target_xid(const char *newval, void *extra)
5116 : : {
5117 [ + - + + ]: 1355 : if (newval && strcmp(newval, "") != 0)
5118 : 5 : recoveryTargetXid = *((TransactionId *) extra);
5119 : 1355 : }
|