Age Owner 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
368 sfrost 62 GIC 4 : pg_backup_start(PG_FUNCTION_ARGS)
63 : {
2219 noah 64 CBC 4 : text *backupid = PG_GETARG_TEXT_PP(0);
4174 simon 65 GIC 4 : bool fast = PG_GETARG_BOOL(1);
4174 simon 66 ECB : char *backupidstr;
2207 teodor 67 GIC 4 : SessionBackupState status = get_backup_status();
368 sfrost 68 ECB : MemoryContext oldcontext;
69 :
4174 simon 70 GIC 4 : backupidstr = text_to_cstring(backupid);
4174 simon 71 ECB :
368 sfrost 72 GIC 4 : if (status == SESSION_BACKUP_RUNNING)
2560 magnus 73 LBC 0 : ereport(ERROR,
2560 magnus 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 : */
169 michael 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 : {
169 michael 92 UNC 0 : backup_state = NULL;
195 93 0 : tablespace_map = NULL;
169 94 0 : MemoryContextReset(backupcontext);
95 : }
96 :
169 michael 97 GNC 4 : oldcontext = MemoryContextSwitchTo(backupcontext);
98 4 : backup_state = (BackupState *) palloc0(sizeof(BackupState));
195 99 4 : tablespace_map = makeStringInfo();
368 sfrost 100 GIC 4 : MemoryContextSwitchTo(oldcontext);
2560 magnus 101 ECB :
368 sfrost 102 GIC 4 : register_persistent_abort_backup_handler();
195 michael 103 GNC 4 : do_pg_backup_start(backupidstr, fast, NULL, backup_state, tablespace_map);
1207 rhaas 104 ECB :
195 michael 105 GNC 3 : PG_RETURN_LSN(backup_state->startpoint);
106 : }
4174 simon 107 EUB :
2560 magnus 108 :
109 : /*
110 : * pg_backup_stop: finish taking an on-line backup.
111 : *
368 sfrost 112 ECB : * The first parameter (variable 'waitforarchive'), which is optional,
2209 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
2559 126 : * GRANT system.
2560 magnus 127 : */
128 : Datum
368 sfrost 129 CBC 2 : pg_backup_stop(PG_FUNCTION_ARGS)
130 : {
131 : #define PG_BACKUP_STOP_V2_COLS 3
132 : TupleDesc tupdesc;
208 michael 133 GNC 2 : Datum values[PG_BACKUP_STOP_V2_COLS] = {0};
134 2 : bool nulls[PG_BACKUP_STOP_V2_COLS] = {0};
368 sfrost 135 GIC 2 : bool waitforarchive = PG_GETARG_BOOL(0);
136 : char *backup_label;
2207 teodor 137 2 : SessionBackupState status = get_backup_status();
138 :
139 : /* Initialize attributes information in the tuple descriptor */
2560 magnus 140 2 : if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
2560 magnus 141 UIC 0 : elog(ERROR, "return type must be a row type");
142 :
368 sfrost 143 GIC 2 : if (status != SESSION_BACKUP_RUNNING)
368 sfrost 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 :
195 michael 149 GNC 2 : Assert(backup_state != NULL);
150 2 : Assert(tablespace_map != NULL);
195 michael 151 ECB :
152 : /* Stop the backup */
195 michael 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);
194 159 2 : values[1] = CStringGetTextDatum(backup_label);
195 160 2 : values[2] = CStringGetTextDatum(tablespace_map->data);
161 :
162 : /* Deallocate backup-related variables */
169 163 2 : pfree(backup_label);
164 :
165 : /* Clean up the session-level state and its memory context */
195 166 2 : backup_state = NULL;
167 2 : tablespace_map = NULL;
169 168 2 : MemoryContextDelete(backupcontext);
169 2 : backupcontext = NULL;
170 :
171 : /* Returns the record as Datum */
402 michael 172 GIC 2 : PG_RETURN_DATUM(HeapTupleGetDatum(heap_form_tuple(tupdesc, values, nulls)));
2560 magnus 173 ECB : }
174 :
175 : /*
176 : * pg_switch_wal: switch to next xlog file
2559 sfrost 177 : *
178 : * Permission checking for this function is managed through the normal
179 : * GRANT system.
4174 simon 180 : */
181 : Datum
2250 rhaas 182 CBC 50 : pg_switch_wal(PG_FUNCTION_ARGS)
4174 simon 183 ECB : {
184 : XLogRecPtr switchpoint;
185 :
4174 simon 186 GIC 50 : if (RecoveryInProgress())
4174 simon 187 LBC 0 : ereport(ERROR,
188 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
189 : errmsg("recovery is in progress"),
4174 simon 190 ECB : errhint("WAL control functions cannot be executed during recovery.")));
191 :
2299 andres 192 CBC 50 : switchpoint = RequestXLogSwitch(false);
4174 simon 193 ECB :
194 : /*
195 : * As a convenience, return the WAL location of the switch record
196 : */
3336 rhaas 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
1 andres 207 GNC 21 : pg_log_standby_snapshot(PG_FUNCTION_ARGS)
208 : {
209 : XLogRecPtr recptr;
210 :
211 21 : if (RecoveryInProgress())
1 andres 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 :
1 andres 217 GNC 21 : if (!XLogStandbyInfoActive())
1 andres 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 :
1 andres 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 : */
4174 simon 236 ECB : Datum
4174 simon 237 GIC 3 : pg_create_restore_point(PG_FUNCTION_ARGS)
238 : {
2219 noah 239 3 : text *restore_name = PG_GETARG_TEXT_PP(0);
4174 simon 240 ECB : char *restore_name_str;
4174 simon 241 EUB : XLogRecPtr restorepoint;
242 :
4174 simon 243 GIC 3 : if (RecoveryInProgress())
4174 simon 244 UIC 0 : ereport(ERROR,
245 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
1165 alvherre 246 ECB : errmsg("recovery is in progress"),
247 : errhint("WAL control functions cannot be executed during recovery.")));
248 :
4174 simon 249 GIC 3 : if (!XLogIsNeeded())
4174 simon 250 UIC 0 : ereport(ERROR,
4174 simon 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 :
4174 simon 255 GIC 3 : restore_name_str = text_to_cstring(restore_name);
256 :
257 3 : if (strlen(restore_name_str) >= MAXFNAMELEN)
4174 simon 258 UIC 0 : ereport(ERROR,
259 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
260 : errmsg("value too long for restore point (maximum %d characters)", MAXFNAMELEN - 1)));
4174 simon 261 ECB :
4174 simon 262 GIC 3 : restorepoint = XLogRestorePoint(restore_name_str);
263 :
264 : /*
4174 simon 265 ECB : * As a convenience, return the WAL location of the restore point record
4174 simon 266 EUB : */
3336 rhaas 267 GIC 3 : PG_RETURN_LSN(restorepoint);
268 : }
269 :
270 : /*
368 sfrost 271 ECB : * Report the current WAL write location (same format as pg_backup_start etc)
4174 simon 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.
4174 simon 276 ECB : */
277 : Datum
2159 tgl 278 GIC 416 : pg_current_wal_lsn(PG_FUNCTION_ARGS)
279 : {
280 : XLogRecPtr current_recptr;
4174 simon 281 ECB :
4174 simon 282 GIC 416 : if (RecoveryInProgress())
4174 simon 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 :
4174 simon 288 GIC 416 : current_recptr = GetXLogWriteRecPtr();
289 :
3336 rhaas 290 416 : PG_RETURN_LSN(current_recptr);
4174 simon 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 : */
4174 simon 298 EUB : Datum
2159 tgl 299 GIC 1072 : pg_current_wal_insert_lsn(PG_FUNCTION_ARGS)
300 : {
301 : XLogRecPtr current_recptr;
302 :
4174 simon 303 CBC 1072 : if (RecoveryInProgress())
4174 simon 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 :
4106 heikki.linnakangas 309 CBC 1072 : current_recptr = GetXLogInsertRecPtr();
310 :
3336 rhaas 311 1072 : PG_RETURN_LSN(current_recptr);
4174 simon 312 EUB : }
313 :
314 : /*
315 : * Report the current WAL flush location (same format as pg_backup_start etc)
2644 simon 316 ECB : *
317 : * This function is mostly for debugging purposes.
318 : */
319 : Datum
2159 tgl 320 GIC 47 : pg_current_wal_flush_lsn(PG_FUNCTION_ARGS)
2644 simon 321 ECB : {
322 : XLogRecPtr current_recptr;
323 :
2644 simon 324 GIC 47 : if (RecoveryInProgress())
2644 simon 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 :
520 rhaas 330 GIC 47 : current_recptr = GetFlushRecPtr(NULL);
331 :
2644 simon 332 CBC 47 : PG_RETURN_LSN(current_recptr);
333 : }
334 :
335 : /*
368 sfrost 336 ECB : * Report the last WAL receive location (same format as pg_backup_start etc)
4174 simon 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
2159 tgl 342 CBC 2 : pg_last_wal_receive_lsn(PG_FUNCTION_ARGS)
343 : {
4174 simon 344 ECB : XLogRecPtr recptr;
345 :
1096 tmunro 346 GIC 2 : recptr = GetWalRcvFlushRecPtr(NULL, NULL);
347 :
3941 heikki.linnakangas 348 2 : if (recptr == 0)
4174 simon 349 UIC 0 : PG_RETURN_NULL();
350 :
3336 rhaas 351 GIC 2 : PG_RETURN_LSN(recptr);
352 : }
4174 simon 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
4174 simon 358 EUB : * connections during recovery.
359 : */
360 : Datum
2159 tgl 361 GIC 25 : pg_last_wal_replay_lsn(PG_FUNCTION_ARGS)
362 : {
4174 simon 363 ECB : XLogRecPtr recptr;
364 :
3762 heikki.linnakangas 365 CBC 25 : recptr = GetXLogReplayRecPtr(NULL);
366 :
3941 heikki.linnakangas 367 GIC 25 : if (recptr == 0)
4174 simon 368 UIC 0 : PG_RETURN_NULL();
369 :
3336 rhaas 370 GIC 25 : PG_RETURN_LSN(recptr);
371 : }
372 :
373 : /*
4174 simon 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
4174 simon 379 EUB : * expected usage is to determine which xlog file(s) are ready to archive.
380 : */
381 : Datum
2250 rhaas 382 UIC 0 : pg_walfile_name_offset(PG_FUNCTION_ARGS)
383 : {
3941 heikki.linnakangas 384 ECB : XLogSegNo xlogsegno;
385 : uint32 xrecoff;
3336 rhaas 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 :
4174 simon 394 UIC 0 : if (RecoveryInProgress())
395 0 : ereport(ERROR,
4174 simon 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
4174 simon 403 EUB : * function's pg_proc entry!
404 : */
1601 andres 405 LBC 0 : resultTupleDesc = CreateTemplateTupleDesc(2);
4174 simon 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
4174 simon 415 ECB : */
2028 andres 416 UIC 0 : XLByteToPrevSeg(locationpoint, xlogsegno, wal_segment_size);
520 rhaas 417 0 : XLogFileName(xlogfilename, GetWALInsertionTimeLine(), xlogsegno,
418 : wal_segment_size);
4174 simon 419 ECB :
4174 simon 420 UIC 0 : values[0] = CStringGetTextDatum(xlogfilename);
4174 simon 421 LBC 0 : isnull[0] = false;
4174 simon 422 EUB :
423 : /*
4174 simon 424 ECB : * offset
425 : */
2028 andres 426 UIC 0 : xrecoff = XLogSegmentOffset(locationpoint, wal_segment_size);
427 :
4174 simon 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 :
4174 simon 436 UBC 0 : result = HeapTupleGetDatum(resultHeapTuple);
437 :
4174 simon 438 UIC 0 : PG_RETURN_DATUM(result);
439 : }
4174 simon 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
2250 rhaas 446 GIC 13 : pg_walfile_name(PG_FUNCTION_ARGS)
447 : {
3941 heikki.linnakangas 448 EUB : XLogSegNo xlogsegno;
3336 rhaas 449 GBC 13 : XLogRecPtr locationpoint = PG_GETARG_LSN(0);
450 : char xlogfilename[MAXFNAMELEN];
451 :
4174 simon 452 GIC 13 : if (RecoveryInProgress())
4174 simon 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 :
2028 andres 459 GBC 13 : XLByteToPrevSeg(locationpoint, xlogsegno, wal_segment_size);
520 rhaas 460 13 : XLogFileName(xlogfilename, GetWALInsertionTimeLine(), xlogsegno,
461 : wal_segment_size);
4174 simon 462 EUB :
4174 simon 463 GIC 13 : PG_RETURN_TEXT_P(cstring_to_text(xlogfilename));
464 : }
4174 simon 465 EUB :
466 : /*
467 : * Extract the sequence number and the timeline ID from given a WAL file
468 : * name.
469 : */
470 : Datum
107 michael 471 GNC 9 : pg_split_walfile_name(PG_FUNCTION_ARGS)
472 : {
473 : #define PG_SPLIT_WALFILE_NAME_COLS 2
110 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;
107 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 :
110 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)
110 michael 500 UNC 0 : elog(ERROR, "return type must be a row type");
501 :
502 : /* Convert to numeric. */
110 michael 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
2559 sfrost 523 EUB : * GRANT system.
4174 simon 524 : */
525 : Datum
2250 rhaas 526 GIC 2 : pg_wal_replay_pause(PG_FUNCTION_ARGS)
4174 simon 527 EUB : {
4174 simon 528 GBC 2 : if (!RecoveryInProgress())
4174 simon 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.")));
4174 simon 533 EUB :
1111 fujii 534 GIC 2 : if (PromoteIsTriggered())
1111 fujii 535 UBC 0 : ereport(ERROR,
1111 fujii 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 :
4174 simon 541 GBC 2 : SetRecoveryPause(true);
542 :
759 rhaas 543 EUB : /* wake up the recovery process so that it can process the pause request */
759 rhaas 544 GIC 2 : WakeupRecovery();
759 rhaas 545 EUB :
4174 simon 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
2559 sfrost 553 ECB : * GRANT system.
554 : */
555 : Datum
2250 rhaas 556 CBC 1 : pg_wal_replay_resume(PG_FUNCTION_ARGS)
557 : {
4174 simon 558 GIC 1 : if (!RecoveryInProgress())
4174 simon 559 LBC 0 : ereport(ERROR,
4174 simon 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 :
1111 fujii 564 GIC 1 : if (PromoteIsTriggered())
1111 fujii 565 UIC 0 : ereport(ERROR,
1111 fujii 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 :
4174 simon 571 GIC 1 : SetRecoveryPause(false);
572 :
573 1 : PG_RETURN_VOID();
574 : }
575 :
576 : /*
577 : * pg_is_wal_replay_paused
4174 simon 578 ECB : */
579 : Datum
2250 rhaas 580 UIC 0 : pg_is_wal_replay_paused(PG_FUNCTION_ARGS)
4174 simon 581 ECB : {
4174 simon 582 UIC 0 : if (!RecoveryInProgress())
583 0 : ereport(ERROR,
584 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
585 : errmsg("recovery is not in progress"),
4174 simon 586 ECB : errhint("Recovery control functions can only be executed during recovery.")));
587 :
759 rhaas 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.
759 rhaas 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
759 rhaas 601 GIC 4 : pg_get_wal_replay_pause_state(PG_FUNCTION_ARGS)
602 : {
697 tgl 603 4 : char *statestr = NULL;
759 rhaas 604 ECB :
759 rhaas 605 GIC 4 : if (!RecoveryInProgress())
759 rhaas 606 LBC 0 : ereport(ERROR,
759 rhaas 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.")));
759 rhaas 610 ECB :
611 : /* get the recovery pause state */
697 tgl 612 GIC 4 : switch (GetRecoveryPauseState())
613 : {
759 rhaas 614 2 : case RECOVERY_NOT_PAUSED:
615 2 : statestr = "not paused";
759 rhaas 616 CBC 2 : break;
759 rhaas 617 UIC 0 : case RECOVERY_PAUSE_REQUESTED:
759 rhaas 618 LBC 0 : statestr = "pause requested";
619 0 : break;
759 rhaas 620 GIC 2 : case RECOVERY_PAUSED:
759 rhaas 621 CBC 2 : statestr = "paused";
759 rhaas 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
4174 simon 633 ECB : * returns NULL.
634 : */
635 : Datum
4174 simon 636 UBC 0 : pg_last_xact_replay_timestamp(PG_FUNCTION_ARGS)
637 : {
638 : TimestampTz xtime;
639 :
4174 simon 640 UIC 0 : xtime = GetLatestXTime();
4174 simon 641 LBC 0 : if (xtime == 0)
4174 simon 642 UBC 0 : PG_RETURN_NULL();
643 :
4174 simon 644 UIC 0 : PG_RETURN_TIMESTAMPTZ(xtime);
645 : }
646 :
647 : /*
4174 simon 648 ECB : * Returns bool with current recovery mode, a global state.
649 : */
650 : Datum
4174 simon 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
2159 tgl 660 GIC 3 : pg_wal_lsn_diff(PG_FUNCTION_ARGS)
661 : {
662 : Datum result;
4053 magnus 663 ECB :
3336 rhaas 664 GIC 3 : result = DirectFunctionCall2(pg_lsn_mi,
3336 rhaas 665 ECB : PG_GETARG_DATUM(0),
3336 rhaas 666 EUB : PG_GETARG_DATUM(1));
667 :
224 peter 668 GNC 3 : PG_RETURN_DATUM(result);
669 : }
670 :
1627 michael 671 ECB : /*
1627 michael 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
1627 michael 678 CBC 1 : pg_promote(PG_FUNCTION_ARGS)
679 : {
680 1 : bool wait = PG_GETARG_BOOL(0);
1627 michael 681 GIC 1 : int wait_seconds = PG_GETARG_INT32(1);
682 : FILE *promote_file;
683 : int i;
684 :
685 1 : if (!RecoveryInProgress())
1627 michael 686 UIC 0 : ereport(ERROR,
1627 michael 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 :
1627 michael 691 GIC 1 : if (wait_seconds <= 0)
1627 michael 692 UIC 0 : ereport(ERROR,
693 : (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
694 : errmsg("\"wait_seconds\" must not be negative or zero")));
1627 michael 695 EUB :
696 : /* create the promote signal file */
1627 michael 697 GIC 1 : promote_file = AllocateFile(PROMOTE_SIGNAL_FILE, "w");
698 1 : if (!promote_file)
1627 michael 699 UIC 0 : ereport(ERROR,
700 : (errcode_for_file_access(),
701 : errmsg("could not create file \"%s\": %m",
702 : PROMOTE_SIGNAL_FILE)));
703 :
1627 michael 704 GIC 1 : if (FreeFile(promote_file))
1627 michael 705 UIC 0 : ereport(ERROR,
706 : (errcode_for_file_access(),
707 : errmsg("could not write file \"%s\": %m",
1627 michael 708 ECB : PROMOTE_SIGNAL_FILE)));
709 :
710 : /* signal the postmaster */
1627 michael 711 GIC 1 : if (kill(PostmasterPid, SIGUSR1) != 0)
1627 michael 712 ECB : {
1627 michael 713 UBC 0 : ereport(WARNING,
714 : (errmsg("failed to send signal to postmaster: %m")));
1627 michael 715 UIC 0 : (void) unlink(PROMOTE_SIGNAL_FILE);
716 0 : PG_RETURN_BOOL(false);
717 : }
718 :
1627 michael 719 ECB : /* return immediately if waiting was not requested */
1627 michael 720 GIC 1 : if (!wait)
1627 michael 721 LBC 0 : PG_RETURN_BOOL(true);
1627 michael 722 ECB :
723 : /* wait for the amount of time wanted until promotion */
1627 michael 724 EUB : #define WAITS_PER_SECOND 10
1627 michael 725 GBC 6 : for (i = 0; i < WAITS_PER_SECOND * wait_seconds; i++)
1627 michael 726 EUB : {
1311 fujii 727 ECB : int rc;
728 :
1627 michael 729 CBC 6 : ResetLatch(MyLatch);
730 :
1627 michael 731 GIC 6 : if (!RecoveryInProgress())
1627 michael 732 CBC 1 : PG_RETURN_BOOL(true);
1627 michael 733 ECB :
1627 michael 734 GIC 5 : CHECK_FOR_INTERRUPTS();
735 :
1311 fujii 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
1311 fujii 743 EUB : * necessity for manual cleanup of all postmaster children.
744 : */
1311 fujii 745 GIC 5 : if (rc & WL_POSTMASTER_DEATH)
1311 fujii 746 UIC 0 : PG_RETURN_BOOL(false);
1627 michael 747 EUB : }
748 :
1627 michael 749 UBC 0 : ereport(WARNING,
750 : (errmsg_plural("server did not promote within %d second",
788 peter 751 EUB : "server did not promote within %d seconds",
752 : wait_seconds,
753 : wait_seconds)));
1627 michael 754 UIC 0 : PG_RETURN_BOOL(false);
755 : }
|