TLA Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * xlogfuncs.c
4 : *
5 : * PostgreSQL write-ahead log manager user interface functions
6 : *
7 : * This file contains WAL control and information functions.
8 : *
9 : *
10 : * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
11 : * Portions Copyright (c) 1994, Regents of the University of California
12 : *
13 : * src/backend/access/transam/xlogfuncs.c
14 : *
15 : *-------------------------------------------------------------------------
16 : */
17 : #include "postgres.h"
18 :
19 : #include <unistd.h>
20 :
21 : #include "access/htup_details.h"
22 : #include "access/xlog_internal.h"
23 : #include "access/xlogbackup.h"
24 : #include "access/xlogrecovery.h"
25 : #include "access/xlogutils.h"
26 : #include "catalog/pg_type.h"
27 : #include "funcapi.h"
28 : #include "miscadmin.h"
29 : #include "pgstat.h"
30 : #include "replication/walreceiver.h"
31 : #include "storage/fd.h"
32 : #include "storage/ipc.h"
33 : #include "storage/smgr.h"
34 : #include "storage/standby.h"
35 : #include "utils/builtins.h"
36 : #include "utils/guc.h"
37 : #include "utils/memutils.h"
38 : #include "utils/numeric.h"
39 : #include "utils/pg_lsn.h"
40 : #include "utils/timestamp.h"
41 : #include "utils/tuplestore.h"
42 :
43 : /*
44 : * Backup-related variables.
45 : */
46 : static BackupState *backup_state = NULL;
47 : static StringInfo tablespace_map = NULL;
48 :
49 : /* Session-level context for the SQL-callable backup functions */
50 : static MemoryContext backupcontext = NULL;
51 :
52 : /*
53 : * pg_backup_start: set up for taking an on-line backup dump
54 : *
55 : * Essentially what this does is to create the contents required for the
56 : * backup_label file and the tablespace map.
57 : *
58 : * Permission checking for this function is managed through the normal
59 : * GRANT system.
60 : */
61 : Datum
62 GIC 4 : pg_backup_start(PG_FUNCTION_ARGS)
63 : {
64 CBC 4 : text *backupid = PG_GETARG_TEXT_PP(0);
65 GIC 4 : bool fast = PG_GETARG_BOOL(1);
66 ECB : char *backupidstr;
67 GIC 4 : SessionBackupState status = get_backup_status();
68 ECB : MemoryContext oldcontext;
69 :
70 GIC 4 : backupidstr = text_to_cstring(backupid);
71 ECB :
72 GIC 4 : if (status == SESSION_BACKUP_RUNNING)
73 LBC 0 : ereport(ERROR,
74 EUB : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
75 : errmsg("a backup is already in progress in this session")));
76 :
77 : /*
78 : * backup_state and tablespace_map need to be long-lived as they are used
79 : * in pg_backup_stop(). These are allocated in a dedicated memory context
80 : * child of TopMemoryContext, deleted at the end of pg_backup_stop(). If
81 : * an error happens before ending the backup, memory would be leaked in
82 : * this context until pg_backup_start() is called again.
83 : */
84 GNC 4 : if (backupcontext == NULL)
85 : {
86 4 : backupcontext = AllocSetContextCreate(TopMemoryContext,
87 : "on-line backup context",
88 : ALLOCSET_START_SMALL_SIZES);
89 : }
90 : else
91 : {
92 UNC 0 : backup_state = NULL;
93 0 : tablespace_map = NULL;
94 0 : MemoryContextReset(backupcontext);
95 : }
96 :
97 GNC 4 : oldcontext = MemoryContextSwitchTo(backupcontext);
98 4 : backup_state = (BackupState *) palloc0(sizeof(BackupState));
99 4 : tablespace_map = makeStringInfo();
100 GIC 4 : MemoryContextSwitchTo(oldcontext);
101 ECB :
102 GIC 4 : register_persistent_abort_backup_handler();
103 GNC 4 : do_pg_backup_start(backupidstr, fast, NULL, backup_state, tablespace_map);
104 ECB :
105 GNC 3 : PG_RETURN_LSN(backup_state->startpoint);
106 : }
107 EUB :
108 :
109 : /*
110 : * pg_backup_stop: finish taking an on-line backup.
111 : *
112 ECB : * The first parameter (variable 'waitforarchive'), which is optional,
113 : * allows the user to choose if they want to wait for the WAL to be archived
114 : * or if we should just return as soon as the WAL record is written.
115 : *
116 : * This function stops an in-progress backup, creates backup_label contents and
117 : * it returns the backup stop LSN, backup_label and tablespace_map contents.
118 : *
119 : * The backup_label contains the user-supplied label string (typically this
120 : * would be used to tell where the backup dump will be stored), the starting
121 : * time, starting WAL location for the dump and so on. It is the caller's
122 : * responsibility to write the backup_label and tablespace_map files in the
123 : * data folder that will be restored from this backup.
124 : *
125 : * Permission checking for this function is managed through the normal
126 : * GRANT system.
127 : */
128 : Datum
129 CBC 2 : pg_backup_stop(PG_FUNCTION_ARGS)
130 : {
131 : #define PG_BACKUP_STOP_V2_COLS 3
132 : TupleDesc tupdesc;
133 GNC 2 : Datum values[PG_BACKUP_STOP_V2_COLS] = {0};
134 2 : bool nulls[PG_BACKUP_STOP_V2_COLS] = {0};
135 GIC 2 : bool waitforarchive = PG_GETARG_BOOL(0);
136 : char *backup_label;
137 2 : SessionBackupState status = get_backup_status();
138 :
139 : /* Initialize attributes information in the tuple descriptor */
140 2 : if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
141 UIC 0 : elog(ERROR, "return type must be a row type");
142 :
143 GIC 2 : if (status != SESSION_BACKUP_RUNNING)
144 UIC 0 : ereport(ERROR,
145 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
146 : errmsg("backup is not in progress"),
147 : errhint("Did you call pg_backup_start()?")));
148 :
149 GNC 2 : Assert(backup_state != NULL);
150 2 : Assert(tablespace_map != NULL);
151 ECB :
152 : /* Stop the backup */
153 GNC 2 : do_pg_backup_stop(backup_state, waitforarchive);
154 :
155 : /* Build the contents of backup_label */
156 2 : backup_label = build_backup_content(backup_state, false);
157 :
158 2 : values[0] = LSNGetDatum(backup_state->stoppoint);
159 2 : values[1] = CStringGetTextDatum(backup_label);
160 2 : values[2] = CStringGetTextDatum(tablespace_map->data);
161 :
162 : /* Deallocate backup-related variables */
163 2 : pfree(backup_label);
164 :
165 : /* Clean up the session-level state and its memory context */
166 2 : backup_state = NULL;
167 2 : tablespace_map = NULL;
168 2 : MemoryContextDelete(backupcontext);
169 2 : backupcontext = NULL;
170 :
171 : /* Returns the record as Datum */
172 GIC 2 : PG_RETURN_DATUM(HeapTupleGetDatum(heap_form_tuple(tupdesc, values, nulls)));
173 ECB : }
174 :
175 : /*
176 : * pg_switch_wal: switch to next xlog file
177 : *
178 : * Permission checking for this function is managed through the normal
179 : * GRANT system.
180 : */
181 : Datum
182 CBC 50 : pg_switch_wal(PG_FUNCTION_ARGS)
183 ECB : {
184 : XLogRecPtr switchpoint;
185 :
186 GIC 50 : if (RecoveryInProgress())
187 LBC 0 : ereport(ERROR,
188 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
189 : errmsg("recovery is in progress"),
190 ECB : errhint("WAL control functions cannot be executed during recovery.")));
191 :
192 CBC 50 : switchpoint = RequestXLogSwitch(false);
193 ECB :
194 : /*
195 : * As a convenience, return the WAL location of the switch record
196 : */
197 GIC 50 : PG_RETURN_LSN(switchpoint);
198 : }
199 :
200 : /*
201 : * pg_log_standby_snapshot: call LogStandbySnapshot()
202 : *
203 : * Permission checking for this function is managed through the normal
204 : * GRANT system.
205 : */
206 : Datum
207 GNC 21 : pg_log_standby_snapshot(PG_FUNCTION_ARGS)
208 : {
209 : XLogRecPtr recptr;
210 :
211 21 : if (RecoveryInProgress())
212 UNC 0 : ereport(ERROR,
213 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
214 : errmsg("recovery is in progress"),
215 : errhint("pg_log_standby_snapshot() cannot be executed during recovery.")));
216 :
217 GNC 21 : if (!XLogStandbyInfoActive())
218 UNC 0 : ereport(ERROR,
219 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
220 : errmsg("pg_log_standby_snapshot() can only be used if wal_level >= replica")));
221 :
222 GNC 21 : recptr = LogStandbySnapshot();
223 :
224 : /*
225 : * As a convenience, return the WAL location of the last inserted record
226 : */
227 21 : PG_RETURN_LSN(recptr);
228 : }
229 :
230 : /*
231 : * pg_create_restore_point: a named point for restore
232 : *
233 : * Permission checking for this function is managed through the normal
234 : * GRANT system.
235 : */
236 ECB : Datum
237 GIC 3 : pg_create_restore_point(PG_FUNCTION_ARGS)
238 : {
239 3 : text *restore_name = PG_GETARG_TEXT_PP(0);
240 ECB : char *restore_name_str;
241 EUB : XLogRecPtr restorepoint;
242 :
243 GIC 3 : if (RecoveryInProgress())
244 UIC 0 : ereport(ERROR,
245 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
246 ECB : errmsg("recovery is in progress"),
247 : errhint("WAL control functions cannot be executed during recovery.")));
248 :
249 GIC 3 : if (!XLogIsNeeded())
250 UIC 0 : ereport(ERROR,
251 ECB : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
252 : errmsg("WAL level not sufficient for creating a restore point"),
253 : errhint("wal_level must be set to \"replica\" or \"logical\" at server start.")));
254 :
255 GIC 3 : restore_name_str = text_to_cstring(restore_name);
256 :
257 3 : if (strlen(restore_name_str) >= MAXFNAMELEN)
258 UIC 0 : ereport(ERROR,
259 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
260 : errmsg("value too long for restore point (maximum %d characters)", MAXFNAMELEN - 1)));
261 ECB :
262 GIC 3 : restorepoint = XLogRestorePoint(restore_name_str);
263 :
264 : /*
265 ECB : * As a convenience, return the WAL location of the restore point record
266 EUB : */
267 GIC 3 : PG_RETURN_LSN(restorepoint);
268 : }
269 :
270 : /*
271 ECB : * Report the current WAL write location (same format as pg_backup_start etc)
272 EUB : *
273 : * This is useful for determining how much of WAL is visible to an external
274 : * archiving process. Note that the data before this point is written out
275 : * to the kernel, but is not necessarily synced to disk.
276 ECB : */
277 : Datum
278 GIC 416 : pg_current_wal_lsn(PG_FUNCTION_ARGS)
279 : {
280 : XLogRecPtr current_recptr;
281 ECB :
282 GIC 416 : if (RecoveryInProgress())
283 UIC 0 : ereport(ERROR,
284 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
285 : errmsg("recovery is in progress"),
286 : errhint("WAL control functions cannot be executed during recovery.")));
287 :
288 GIC 416 : current_recptr = GetXLogWriteRecPtr();
289 :
290 416 : PG_RETURN_LSN(current_recptr);
291 ECB : }
292 :
293 : /*
294 : * Report the current WAL insert location (same format as pg_backup_start etc)
295 : *
296 : * This function is mostly for debugging purposes.
297 : */
298 EUB : Datum
299 GIC 1072 : pg_current_wal_insert_lsn(PG_FUNCTION_ARGS)
300 : {
301 : XLogRecPtr current_recptr;
302 :
303 CBC 1072 : if (RecoveryInProgress())
304 UBC 0 : ereport(ERROR,
305 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
306 : errmsg("recovery is in progress"),
307 : errhint("WAL control functions cannot be executed during recovery.")));
308 :
309 CBC 1072 : current_recptr = GetXLogInsertRecPtr();
310 :
311 1072 : PG_RETURN_LSN(current_recptr);
312 EUB : }
313 :
314 : /*
315 : * Report the current WAL flush location (same format as pg_backup_start etc)
316 ECB : *
317 : * This function is mostly for debugging purposes.
318 : */
319 : Datum
320 GIC 47 : pg_current_wal_flush_lsn(PG_FUNCTION_ARGS)
321 ECB : {
322 : XLogRecPtr current_recptr;
323 :
324 GIC 47 : if (RecoveryInProgress())
325 UIC 0 : ereport(ERROR,
326 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
327 : errmsg("recovery is in progress"),
328 : errhint("WAL control functions cannot be executed during recovery.")));
329 :
330 GIC 47 : current_recptr = GetFlushRecPtr(NULL);
331 :
332 CBC 47 : PG_RETURN_LSN(current_recptr);
333 : }
334 :
335 : /*
336 ECB : * Report the last WAL receive location (same format as pg_backup_start etc)
337 EUB : *
338 : * This is useful for determining how much of WAL is guaranteed to be received
339 : * and synced to disk by walreceiver.
340 : */
341 : Datum
342 CBC 2 : pg_last_wal_receive_lsn(PG_FUNCTION_ARGS)
343 : {
344 ECB : XLogRecPtr recptr;
345 :
346 GIC 2 : recptr = GetWalRcvFlushRecPtr(NULL, NULL);
347 :
348 2 : if (recptr == 0)
349 UIC 0 : PG_RETURN_NULL();
350 :
351 GIC 2 : PG_RETURN_LSN(recptr);
352 : }
353 ECB :
354 : /*
355 : * Report the last WAL replay location (same format as pg_backup_start etc)
356 : *
357 : * This is useful for determining how much of WAL is visible to read-only
358 EUB : * connections during recovery.
359 : */
360 : Datum
361 GIC 25 : pg_last_wal_replay_lsn(PG_FUNCTION_ARGS)
362 : {
363 ECB : XLogRecPtr recptr;
364 :
365 CBC 25 : recptr = GetXLogReplayRecPtr(NULL);
366 :
367 GIC 25 : if (recptr == 0)
368 UIC 0 : PG_RETURN_NULL();
369 :
370 GIC 25 : PG_RETURN_LSN(recptr);
371 : }
372 :
373 : /*
374 ECB : * Compute an xlog file name and decimal byte offset given a WAL location,
375 : * such as is returned by pg_backup_stop() or pg_switch_wal().
376 : *
377 : * Note that a location exactly at a segment boundary is taken to be in
378 : * the previous segment. This is usually the right thing, since the
379 EUB : * expected usage is to determine which xlog file(s) are ready to archive.
380 : */
381 : Datum
382 UIC 0 : pg_walfile_name_offset(PG_FUNCTION_ARGS)
383 : {
384 ECB : XLogSegNo xlogsegno;
385 : uint32 xrecoff;
386 LBC 0 : XLogRecPtr locationpoint = PG_GETARG_LSN(0);
387 : char xlogfilename[MAXFNAMELEN];
388 : Datum values[2];
389 : bool isnull[2];
390 : TupleDesc resultTupleDesc;
391 : HeapTuple resultHeapTuple;
392 : Datum result;
393 :
394 UIC 0 : if (RecoveryInProgress())
395 0 : ereport(ERROR,
396 ECB : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
397 : errmsg("recovery is in progress"),
398 : errhint("%s cannot be executed during recovery.",
399 : "pg_walfile_name_offset()")));
400 :
401 : /*
402 : * Construct a tuple descriptor for the result row. This must match this
403 EUB : * function's pg_proc entry!
404 : */
405 LBC 0 : resultTupleDesc = CreateTemplateTupleDesc(2);
406 UIC 0 : TupleDescInitEntry(resultTupleDesc, (AttrNumber) 1, "file_name",
407 : TEXTOID, -1, 0);
408 0 : TupleDescInitEntry(resultTupleDesc, (AttrNumber) 2, "file_offset",
409 : INT4OID, -1, 0);
410 :
411 0 : resultTupleDesc = BlessTupleDesc(resultTupleDesc);
412 :
413 : /*
414 : * xlogfilename
415 ECB : */
416 UIC 0 : XLByteToPrevSeg(locationpoint, xlogsegno, wal_segment_size);
417 0 : XLogFileName(xlogfilename, GetWALInsertionTimeLine(), xlogsegno,
418 : wal_segment_size);
419 ECB :
420 UIC 0 : values[0] = CStringGetTextDatum(xlogfilename);
421 LBC 0 : isnull[0] = false;
422 EUB :
423 : /*
424 ECB : * offset
425 : */
426 UIC 0 : xrecoff = XLogSegmentOffset(locationpoint, wal_segment_size);
427 :
428 0 : values[1] = UInt32GetDatum(xrecoff);
429 0 : isnull[1] = false;
430 :
431 : /*
432 : * Tuple jam: Having first prepared your Datums, then squash together
433 : */
434 0 : resultHeapTuple = heap_form_tuple(resultTupleDesc, values, isnull);
435 :
436 UBC 0 : result = HeapTupleGetDatum(resultHeapTuple);
437 :
438 UIC 0 : PG_RETURN_DATUM(result);
439 : }
440 EUB :
441 : /*
442 : * Compute an xlog file name given a WAL location,
443 : * such as is returned by pg_backup_stop() or pg_switch_wal().
444 : */
445 : Datum
446 GIC 13 : pg_walfile_name(PG_FUNCTION_ARGS)
447 : {
448 EUB : XLogSegNo xlogsegno;
449 GBC 13 : XLogRecPtr locationpoint = PG_GETARG_LSN(0);
450 : char xlogfilename[MAXFNAMELEN];
451 :
452 GIC 13 : if (RecoveryInProgress())
453 UIC 0 : ereport(ERROR,
454 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
455 : errmsg("recovery is in progress"),
456 : errhint("%s cannot be executed during recovery.",
457 : "pg_walfile_name()")));
458 :
459 GBC 13 : XLByteToPrevSeg(locationpoint, xlogsegno, wal_segment_size);
460 13 : XLogFileName(xlogfilename, GetWALInsertionTimeLine(), xlogsegno,
461 : wal_segment_size);
462 EUB :
463 GIC 13 : PG_RETURN_TEXT_P(cstring_to_text(xlogfilename));
464 : }
465 EUB :
466 : /*
467 : * Extract the sequence number and the timeline ID from given a WAL file
468 : * name.
469 : */
470 : Datum
471 GNC 9 : pg_split_walfile_name(PG_FUNCTION_ARGS)
472 : {
473 : #define PG_SPLIT_WALFILE_NAME_COLS 2
474 9 : char *fname = text_to_cstring(PG_GETARG_TEXT_PP(0));
475 : char *fname_upper;
476 : char *p;
477 : TimeLineID tli;
478 : XLogSegNo segno;
479 9 : Datum values[PG_SPLIT_WALFILE_NAME_COLS] = {0};
480 9 : bool isnull[PG_SPLIT_WALFILE_NAME_COLS] = {0};
481 : TupleDesc tupdesc;
482 : HeapTuple tuple;
483 : char buf[256];
484 : Datum result;
485 :
486 9 : fname_upper = pstrdup(fname);
487 :
488 : /* Capitalize WAL file name. */
489 174 : for (p = fname_upper; *p; p++)
490 165 : *p = pg_toupper((unsigned char) *p);
491 :
492 9 : if (!IsXLogFileName(fname_upper))
493 3 : ereport(ERROR,
494 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
495 : errmsg("invalid WAL file name \"%s\"", fname)));
496 :
497 6 : XLogFromFileName(fname_upper, &tli, &segno, wal_segment_size);
498 :
499 6 : if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
500 UNC 0 : elog(ERROR, "return type must be a row type");
501 :
502 : /* Convert to numeric. */
503 GNC 6 : snprintf(buf, sizeof buf, UINT64_FORMAT, segno);
504 6 : values[0] = DirectFunctionCall3(numeric_in,
505 : CStringGetDatum(buf),
506 : ObjectIdGetDatum(0),
507 : Int32GetDatum(-1));
508 :
509 6 : values[1] = Int64GetDatum(tli);
510 :
511 6 : tuple = heap_form_tuple(tupdesc, values, isnull);
512 6 : result = HeapTupleGetDatum(tuple);
513 :
514 6 : PG_RETURN_DATUM(result);
515 :
516 : #undef PG_SPLIT_WALFILE_NAME_COLS
517 : }
518 :
519 : /*
520 : * pg_wal_replay_pause - Request to pause recovery
521 : *
522 : * Permission checking for this function is managed through the normal
523 EUB : * GRANT system.
524 : */
525 : Datum
526 GIC 2 : pg_wal_replay_pause(PG_FUNCTION_ARGS)
527 EUB : {
528 GBC 2 : if (!RecoveryInProgress())
529 UIC 0 : ereport(ERROR,
530 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
531 : errmsg("recovery is not in progress"),
532 : errhint("Recovery control functions can only be executed during recovery.")));
533 EUB :
534 GIC 2 : if (PromoteIsTriggered())
535 UBC 0 : ereport(ERROR,
536 EUB : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
537 : errmsg("standby promotion is ongoing"),
538 : errhint("%s cannot be executed after promotion is triggered.",
539 : "pg_wal_replay_pause()")));
540 :
541 GBC 2 : SetRecoveryPause(true);
542 :
543 EUB : /* wake up the recovery process so that it can process the pause request */
544 GIC 2 : WakeupRecovery();
545 EUB :
546 GIC 2 : PG_RETURN_VOID();
547 : }
548 :
549 : /*
550 : * pg_wal_replay_resume - resume recovery now
551 : *
552 : * Permission checking for this function is managed through the normal
553 ECB : * GRANT system.
554 : */
555 : Datum
556 CBC 1 : pg_wal_replay_resume(PG_FUNCTION_ARGS)
557 : {
558 GIC 1 : if (!RecoveryInProgress())
559 LBC 0 : ereport(ERROR,
560 EUB : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
561 : errmsg("recovery is not in progress"),
562 : errhint("Recovery control functions can only be executed during recovery.")));
563 :
564 GIC 1 : if (PromoteIsTriggered())
565 UIC 0 : ereport(ERROR,
566 ECB : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
567 : errmsg("standby promotion is ongoing"),
568 : errhint("%s cannot be executed after promotion is triggered.",
569 : "pg_wal_replay_resume()")));
570 :
571 GIC 1 : SetRecoveryPause(false);
572 :
573 1 : PG_RETURN_VOID();
574 : }
575 :
576 : /*
577 : * pg_is_wal_replay_paused
578 ECB : */
579 : Datum
580 UIC 0 : pg_is_wal_replay_paused(PG_FUNCTION_ARGS)
581 ECB : {
582 UIC 0 : if (!RecoveryInProgress())
583 0 : ereport(ERROR,
584 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
585 : errmsg("recovery is not in progress"),
586 ECB : errhint("Recovery control functions can only be executed during recovery.")));
587 :
588 UIC 0 : PG_RETURN_BOOL(GetRecoveryPauseState() != RECOVERY_NOT_PAUSED);
589 : }
590 :
591 : /*
592 : * pg_get_wal_replay_pause_state - Returns the recovery pause state.
593 ECB : *
594 : * Returned values:
595 : *
596 : * 'not paused' - if pause is not requested
597 : * 'pause requested' - if pause is requested but recovery is not yet paused
598 : * 'paused' - if recovery is paused
599 : */
600 : Datum
601 GIC 4 : pg_get_wal_replay_pause_state(PG_FUNCTION_ARGS)
602 : {
603 4 : char *statestr = NULL;
604 ECB :
605 GIC 4 : if (!RecoveryInProgress())
606 LBC 0 : ereport(ERROR,
607 EUB : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
608 : errmsg("recovery is not in progress"),
609 : errhint("Recovery control functions can only be executed during recovery.")));
610 ECB :
611 : /* get the recovery pause state */
612 GIC 4 : switch (GetRecoveryPauseState())
613 : {
614 2 : case RECOVERY_NOT_PAUSED:
615 2 : statestr = "not paused";
616 CBC 2 : break;
617 UIC 0 : case RECOVERY_PAUSE_REQUESTED:
618 LBC 0 : statestr = "pause requested";
619 0 : break;
620 GIC 2 : case RECOVERY_PAUSED:
621 CBC 2 : statestr = "paused";
622 GIC 2 : break;
623 : }
624 :
625 4 : Assert(statestr != NULL);
626 4 : PG_RETURN_TEXT_P(cstring_to_text(statestr));
627 : }
628 :
629 : /*
630 : * Returns timestamp of latest processed commit/abort record.
631 : *
632 : * When the server has been started normally without recovery the function
633 ECB : * returns NULL.
634 : */
635 : Datum
636 UBC 0 : pg_last_xact_replay_timestamp(PG_FUNCTION_ARGS)
637 : {
638 : TimestampTz xtime;
639 :
640 UIC 0 : xtime = GetLatestXTime();
641 LBC 0 : if (xtime == 0)
642 UBC 0 : PG_RETURN_NULL();
643 :
644 UIC 0 : PG_RETURN_TIMESTAMPTZ(xtime);
645 : }
646 :
647 : /*
648 ECB : * Returns bool with current recovery mode, a global state.
649 : */
650 : Datum
651 CBC 194 : pg_is_in_recovery(PG_FUNCTION_ARGS)
652 : {
653 194 : PG_RETURN_BOOL(RecoveryInProgress());
654 : }
655 :
656 : /*
657 : * Compute the difference in bytes between two WAL locations.
658 : */
659 : Datum
660 GIC 3 : pg_wal_lsn_diff(PG_FUNCTION_ARGS)
661 : {
662 : Datum result;
663 ECB :
664 GIC 3 : result = DirectFunctionCall2(pg_lsn_mi,
665 ECB : PG_GETARG_DATUM(0),
666 EUB : PG_GETARG_DATUM(1));
667 :
668 GNC 3 : PG_RETURN_DATUM(result);
669 : }
670 :
671 ECB : /*
672 EUB : * Promotes a standby server.
673 : *
674 : * A result of "true" means that promotion has been completed if "wait" is
675 : * "true", or initiated if "wait" is false.
676 : */
677 : Datum
678 CBC 1 : pg_promote(PG_FUNCTION_ARGS)
679 : {
680 1 : bool wait = PG_GETARG_BOOL(0);
681 GIC 1 : int wait_seconds = PG_GETARG_INT32(1);
682 : FILE *promote_file;
683 : int i;
684 :
685 1 : if (!RecoveryInProgress())
686 UIC 0 : ereport(ERROR,
687 EUB : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
688 : errmsg("recovery is not in progress"),
689 : errhint("Recovery control functions can only be executed during recovery.")));
690 :
691 GIC 1 : if (wait_seconds <= 0)
692 UIC 0 : ereport(ERROR,
693 : (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
694 : errmsg("\"wait_seconds\" must not be negative or zero")));
695 EUB :
696 : /* create the promote signal file */
697 GIC 1 : promote_file = AllocateFile(PROMOTE_SIGNAL_FILE, "w");
698 1 : if (!promote_file)
699 UIC 0 : ereport(ERROR,
700 : (errcode_for_file_access(),
701 : errmsg("could not create file \"%s\": %m",
702 : PROMOTE_SIGNAL_FILE)));
703 :
704 GIC 1 : if (FreeFile(promote_file))
705 UIC 0 : ereport(ERROR,
706 : (errcode_for_file_access(),
707 : errmsg("could not write file \"%s\": %m",
708 ECB : PROMOTE_SIGNAL_FILE)));
709 :
710 : /* signal the postmaster */
711 GIC 1 : if (kill(PostmasterPid, SIGUSR1) != 0)
712 ECB : {
713 UBC 0 : ereport(WARNING,
714 : (errmsg("failed to send signal to postmaster: %m")));
715 UIC 0 : (void) unlink(PROMOTE_SIGNAL_FILE);
716 0 : PG_RETURN_BOOL(false);
717 : }
718 :
719 ECB : /* return immediately if waiting was not requested */
720 GIC 1 : if (!wait)
721 LBC 0 : PG_RETURN_BOOL(true);
722 ECB :
723 : /* wait for the amount of time wanted until promotion */
724 EUB : #define WAITS_PER_SECOND 10
725 GBC 6 : for (i = 0; i < WAITS_PER_SECOND * wait_seconds; i++)
726 EUB : {
727 ECB : int rc;
728 :
729 CBC 6 : ResetLatch(MyLatch);
730 :
731 GIC 6 : if (!RecoveryInProgress())
732 CBC 1 : PG_RETURN_BOOL(true);
733 ECB :
734 GIC 5 : CHECK_FOR_INTERRUPTS();
735 :
736 5 : rc = WaitLatch(MyLatch,
737 : WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
738 : 1000L / WAITS_PER_SECOND,
739 : WAIT_EVENT_PROMOTE);
740 :
741 : /*
742 : * Emergency bailout if postmaster has died. This is to avoid the
743 EUB : * necessity for manual cleanup of all postmaster children.
744 : */
745 GIC 5 : if (rc & WL_POSTMASTER_DEATH)
746 UIC 0 : PG_RETURN_BOOL(false);
747 EUB : }
748 :
749 UBC 0 : ereport(WARNING,
750 : (errmsg_plural("server did not promote within %d second",
751 EUB : "server did not promote within %d seconds",
752 : wait_seconds,
753 : wait_seconds)));
754 UIC 0 : PG_RETURN_BOOL(false);
755 : }
|