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