Coverage Report

Created: 2026-08-13 07:12

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