LCOV - code coverage report
Current view: top level - src/backend/access/transam - xlogfuncs.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 79.1 % 249 197
Test Date: 2026-03-24 01:16:09 Functions: 95.7 % 23 22
Legend: Lines:     hit not hit

            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-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            6 : GetRecoveryPauseStateString(RecoveryPauseState pause_state)
      57              : {
      58            6 :     const char *statestr = NULL;
      59              : 
      60            6 :     switch (pause_state)
      61              :     {
      62            3 :         case RECOVERY_NOT_PAUSED:
      63            3 :             statestr = "not paused";
      64            3 :             break;
      65            0 :         case RECOVERY_PAUSE_REQUESTED:
      66            0 :             statestr = "pause requested";
      67            0 :             break;
      68            3 :         case RECOVERY_PAUSED:
      69            3 :             statestr = "paused";
      70            3 :             break;
      71              :     }
      72              : 
      73              :     Assert(statestr != NULL);
      74            6 :     return statestr;
      75              : }
      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            5 : pg_backup_start(PG_FUNCTION_ARGS)
      88              : {
      89            5 :     text       *backupid = PG_GETARG_TEXT_PP(0);
      90            5 :     bool        fast = PG_GETARG_BOOL(1);
      91              :     char       *backupidstr;
      92            5 :     SessionBackupState status = get_backup_status();
      93              :     MemoryContext oldcontext;
      94              : 
      95            5 :     backupidstr = text_to_cstring(backupid);
      96              : 
      97            5 :     if (status == SESSION_BACKUP_RUNNING)
      98            0 :         ereport(ERROR,
      99              :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
     100              :                  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            5 :     if (backupcontext == NULL)
     110              :     {
     111            5 :         backupcontext = AllocSetContextCreate(TopMemoryContext,
     112              :                                               "on-line backup context",
     113              :                                               ALLOCSET_START_SMALL_SIZES);
     114              :     }
     115              :     else
     116              :     {
     117            0 :         backup_state = NULL;
     118            0 :         tablespace_map = NULL;
     119            0 :         MemoryContextReset(backupcontext);
     120              :     }
     121              : 
     122            5 :     oldcontext = MemoryContextSwitchTo(backupcontext);
     123            5 :     backup_state = palloc0_object(BackupState);
     124            5 :     tablespace_map = makeStringInfo();
     125            5 :     MemoryContextSwitchTo(oldcontext);
     126              : 
     127            5 :     register_persistent_abort_backup_handler();
     128            5 :     do_pg_backup_start(backupidstr, fast, NULL, backup_state, tablespace_map);
     129              : 
     130            4 :     PG_RETURN_LSN(backup_state->startpoint);
     131              : }
     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            3 : pg_backup_stop(PG_FUNCTION_ARGS)
     155              : {
     156              : #define PG_BACKUP_STOP_V2_COLS 3
     157              :     TupleDesc   tupdesc;
     158            3 :     Datum       values[PG_BACKUP_STOP_V2_COLS] = {0};
     159            3 :     bool        nulls[PG_BACKUP_STOP_V2_COLS] = {0};
     160            3 :     bool        waitforarchive = PG_GETARG_BOOL(0);
     161              :     char       *backup_label;
     162            3 :     SessionBackupState status = get_backup_status();
     163              : 
     164              :     /* Initialize attributes information in the tuple descriptor */
     165            3 :     if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
     166            0 :         elog(ERROR, "return type must be a row type");
     167              : 
     168            3 :     if (status != SESSION_BACKUP_RUNNING)
     169            0 :         ereport(ERROR,
     170              :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
     171              :                  errmsg("backup is not in progress"),
     172              :                  errhint("Did you call pg_backup_start()?")));
     173              : 
     174              :     Assert(backup_state != NULL);
     175              :     Assert(tablespace_map != NULL);
     176              : 
     177              :     /* Stop the backup */
     178            3 :     do_pg_backup_stop(backup_state, waitforarchive);
     179              : 
     180              :     /* Build the contents of backup_label */
     181            3 :     backup_label = build_backup_content(backup_state, false);
     182              : 
     183            3 :     values[0] = LSNGetDatum(backup_state->stoppoint);
     184            3 :     values[1] = CStringGetTextDatum(backup_label);
     185            3 :     values[2] = CStringGetTextDatum(tablespace_map->data);
     186              : 
     187              :     /* Deallocate backup-related variables */
     188            3 :     pfree(backup_label);
     189              : 
     190              :     /* Clean up the session-level state and its memory context */
     191            3 :     backup_state = NULL;
     192            3 :     tablespace_map = NULL;
     193            3 :     MemoryContextDelete(backupcontext);
     194            3 :     backupcontext = NULL;
     195              : 
     196              :     /* Returns the record as Datum */
     197            3 :     PG_RETURN_DATUM(HeapTupleGetDatum(heap_form_tuple(tupdesc, values, nulls)));
     198              : }
     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          468 : pg_switch_wal(PG_FUNCTION_ARGS)
     208              : {
     209              :     XLogRecPtr  switchpoint;
     210              : 
     211          468 :     if (RecoveryInProgress())
     212            0 :         ereport(ERROR,
     213              :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
     214              :                  errmsg("recovery is in progress"),
     215              :                  errhint("WAL control functions cannot be executed during recovery.")));
     216              : 
     217          468 :     switchpoint = RequestXLogSwitch(false);
     218              : 
     219              :     /*
     220              :      * As a convenience, return the WAL location of the switch record
     221              :      */
     222          468 :     PG_RETURN_LSN(switchpoint);
     223              : }
     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           29 : pg_log_standby_snapshot(PG_FUNCTION_ARGS)
     233              : {
     234              :     XLogRecPtr  recptr;
     235              : 
     236           29 :     if (RecoveryInProgress())
     237            0 :         ereport(ERROR,
     238              :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
     239              :                  errmsg("recovery is in progress"),
     240              :                  errhint("%s cannot be executed during recovery.",
     241              :                          "pg_log_standby_snapshot()")));
     242              : 
     243           29 :     if (!XLogStandbyInfoActive())
     244            0 :         ereport(ERROR,
     245              :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
     246              :                  errmsg("pg_log_standby_snapshot() can only be used if \"wal_level\" >= \"replica\"")));
     247              : 
     248           29 :     recptr = LogStandbySnapshot();
     249              : 
     250              :     /*
     251              :      * As a convenience, return the WAL location of the last inserted record
     252              :      */
     253           29 :     PG_RETURN_LSN(recptr);
     254              : }
     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            3 : pg_create_restore_point(PG_FUNCTION_ARGS)
     264              : {
     265            3 :     text       *restore_name = PG_GETARG_TEXT_PP(0);
     266              :     char       *restore_name_str;
     267              :     XLogRecPtr  restorepoint;
     268              : 
     269            3 :     if (RecoveryInProgress())
     270            0 :         ereport(ERROR,
     271              :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
     272              :                  errmsg("recovery is in progress"),
     273              :                  errhint("WAL control functions cannot be executed during recovery.")));
     274              : 
     275            3 :     if (!XLogIsNeeded())
     276            0 :         ereport(ERROR,
     277              :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
     278              :                  errmsg("WAL level not sufficient for creating a restore point"),
     279              :                  errhint("\"wal_level\" must be set to \"replica\" or \"logical\" at server start.")));
     280              : 
     281            3 :     restore_name_str = text_to_cstring(restore_name);
     282              : 
     283            3 :     if (strlen(restore_name_str) >= MAXFNAMELEN)
     284            0 :         ereport(ERROR,
     285              :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     286              :                  errmsg("value too long for restore point (maximum %d characters)", MAXFNAMELEN - 1)));
     287              : 
     288            3 :     restorepoint = XLogRestorePoint(restore_name_str);
     289              : 
     290              :     /*
     291              :      * As a convenience, return the WAL location of the restore point record
     292              :      */
     293            3 :     PG_RETURN_LSN(restorepoint);
     294              : }
     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          536 : pg_current_wal_lsn(PG_FUNCTION_ARGS)
     305              : {
     306              :     XLogRecPtr  current_recptr;
     307              : 
     308          536 :     if (RecoveryInProgress())
     309            0 :         ereport(ERROR,
     310              :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
     311              :                  errmsg("recovery is in progress"),
     312              :                  errhint("WAL control functions cannot be executed during recovery.")));
     313              : 
     314          536 :     current_recptr = GetXLogWriteRecPtr();
     315              : 
     316          536 :     PG_RETURN_LSN(current_recptr);
     317              : }
     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         1638 : pg_current_wal_insert_lsn(PG_FUNCTION_ARGS)
     326              : {
     327              :     XLogRecPtr  current_recptr;
     328              : 
     329         1638 :     if (RecoveryInProgress())
     330            0 :         ereport(ERROR,
     331              :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
     332              :                  errmsg("recovery is in progress"),
     333              :                  errhint("WAL control functions cannot be executed during recovery.")));
     334              : 
     335         1638 :     current_recptr = GetXLogInsertRecPtr();
     336              : 
     337         1638 :     PG_RETURN_LSN(current_recptr);
     338              : }
     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           91 : pg_current_wal_flush_lsn(PG_FUNCTION_ARGS)
     347              : {
     348              :     XLogRecPtr  current_recptr;
     349              : 
     350           91 :     if (RecoveryInProgress())
     351            0 :         ereport(ERROR,
     352              :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
     353              :                  errmsg("recovery is in progress"),
     354              :                  errhint("WAL control functions cannot be executed during recovery.")));
     355              : 
     356           91 :     current_recptr = GetFlushRecPtr(NULL);
     357              : 
     358           91 :     PG_RETURN_LSN(current_recptr);
     359              : }
     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            5 : pg_last_wal_receive_lsn(PG_FUNCTION_ARGS)
     369              : {
     370              :     XLogRecPtr  recptr;
     371              : 
     372            5 :     recptr = GetWalRcvFlushRecPtr(NULL, NULL);
     373              : 
     374            5 :     if (!XLogRecPtrIsValid(recptr))
     375            0 :         PG_RETURN_NULL();
     376              : 
     377            5 :     PG_RETURN_LSN(recptr);
     378              : }
     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           45 : pg_last_wal_replay_lsn(PG_FUNCTION_ARGS)
     388              : {
     389              :     XLogRecPtr  recptr;
     390              : 
     391           45 :     recptr = GetXLogReplayRecPtr(NULL);
     392              : 
     393           45 :     if (!XLogRecPtrIsValid(recptr))
     394            0 :         PG_RETURN_NULL();
     395              : 
     396           45 :     PG_RETURN_LSN(recptr);
     397              : }
     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           12 : pg_walfile_name_offset(PG_FUNCTION_ARGS)
     405              : {
     406              :     XLogSegNo   xlogsegno;
     407              :     uint32      xrecoff;
     408           12 :     XLogRecPtr  locationpoint = PG_GETARG_LSN(0);
     409              :     char        xlogfilename[MAXFNAMELEN];
     410              :     Datum       values[2];
     411              :     bool        isnull[2];
     412              :     TupleDesc   resultTupleDesc;
     413              :     HeapTuple   resultHeapTuple;
     414              :     Datum       result;
     415              : 
     416           12 :     if (RecoveryInProgress())
     417            0 :         ereport(ERROR,
     418              :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
     419              :                  errmsg("recovery is in progress"),
     420              :                  errhint("%s cannot be executed during recovery.",
     421              :                          "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           12 :     resultTupleDesc = CreateTemplateTupleDesc(2);
     428           12 :     TupleDescInitEntry(resultTupleDesc, (AttrNumber) 1, "file_name",
     429              :                        TEXTOID, -1, 0);
     430           12 :     TupleDescInitEntry(resultTupleDesc, (AttrNumber) 2, "file_offset",
     431              :                        INT4OID, -1, 0);
     432              : 
     433           12 :     TupleDescFinalize(resultTupleDesc);
     434           12 :     resultTupleDesc = BlessTupleDesc(resultTupleDesc);
     435              : 
     436              :     /*
     437              :      * xlogfilename
     438              :      */
     439           12 :     XLByteToSeg(locationpoint, xlogsegno, wal_segment_size);
     440           12 :     XLogFileName(xlogfilename, GetWALInsertionTimeLine(), xlogsegno,
     441              :                  wal_segment_size);
     442              : 
     443           12 :     values[0] = CStringGetTextDatum(xlogfilename);
     444           12 :     isnull[0] = false;
     445              : 
     446              :     /*
     447              :      * offset
     448              :      */
     449           12 :     xrecoff = XLogSegmentOffset(locationpoint, wal_segment_size);
     450              : 
     451           12 :     values[1] = UInt32GetDatum(xrecoff);
     452           12 :     isnull[1] = false;
     453              : 
     454              :     /*
     455              :      * Tuple jam: Having first prepared your Datums, then squash together
     456              :      */
     457           12 :     resultHeapTuple = heap_form_tuple(resultTupleDesc, values, isnull);
     458              : 
     459           12 :     result = HeapTupleGetDatum(resultHeapTuple);
     460              : 
     461           12 :     PG_RETURN_DATUM(result);
     462              : }
     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           24 : pg_walfile_name(PG_FUNCTION_ARGS)
     470              : {
     471              :     XLogSegNo   xlogsegno;
     472           24 :     XLogRecPtr  locationpoint = PG_GETARG_LSN(0);
     473              :     char        xlogfilename[MAXFNAMELEN];
     474              : 
     475           24 :     if (RecoveryInProgress())
     476            0 :         ereport(ERROR,
     477              :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
     478              :                  errmsg("recovery is in progress"),
     479              :                  errhint("%s cannot be executed during recovery.",
     480              :                          "pg_walfile_name()")));
     481              : 
     482           24 :     XLByteToSeg(locationpoint, xlogsegno, wal_segment_size);
     483           24 :     XLogFileName(xlogfilename, GetWALInsertionTimeLine(), xlogsegno,
     484              :                  wal_segment_size);
     485              : 
     486           24 :     PG_RETURN_TEXT_P(cstring_to_text(xlogfilename));
     487              : }
     488              : 
     489              : /*
     490              :  * Extract the sequence number and the timeline ID from given a WAL file
     491              :  * name.
     492              :  */
     493              : Datum
     494           24 : pg_split_walfile_name(PG_FUNCTION_ARGS)
     495              : {
     496              : #define PG_SPLIT_WALFILE_NAME_COLS 2
     497           24 :     char       *fname = text_to_cstring(PG_GETARG_TEXT_PP(0));
     498              :     char       *fname_upper;
     499              :     char       *p;
     500              :     TimeLineID  tli;
     501              :     XLogSegNo   segno;
     502           24 :     Datum       values[PG_SPLIT_WALFILE_NAME_COLS] = {0};
     503           24 :     bool        isnull[PG_SPLIT_WALFILE_NAME_COLS] = {0};
     504              :     TupleDesc   tupdesc;
     505              :     HeapTuple   tuple;
     506              :     char        buf[256];
     507              :     Datum       result;
     508              : 
     509           24 :     fname_upper = pstrdup(fname);
     510              : 
     511              :     /* Capitalize WAL file name. */
     512          532 :     for (p = fname_upper; *p; p++)
     513          508 :         *p = pg_ascii_toupper((unsigned char) *p);
     514              : 
     515           24 :     if (!IsXLogFileName(fname_upper))
     516            4 :         ereport(ERROR,
     517              :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     518              :                  errmsg("invalid WAL file name \"%s\"", fname)));
     519              : 
     520           20 :     XLogFromFileName(fname_upper, &tli, &segno, wal_segment_size);
     521              : 
     522           20 :     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           20 :     snprintf(buf, sizeof buf, UINT64_FORMAT, segno);
     527           20 :     values[0] = DirectFunctionCall3(numeric_in,
     528              :                                     CStringGetDatum(buf),
     529              :                                     ObjectIdGetDatum(0),
     530              :                                     Int32GetDatum(-1));
     531              : 
     532           20 :     values[1] = Int64GetDatum(tli);
     533              : 
     534           20 :     tuple = heap_form_tuple(tupdesc, values, isnull);
     535           20 :     result = HeapTupleGetDatum(tuple);
     536              : 
     537           20 :     PG_RETURN_DATUM(result);
     538              : 
     539              : #undef PG_SPLIT_WALFILE_NAME_COLS
     540              : }
     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            4 : pg_wal_replay_pause(PG_FUNCTION_ARGS)
     550              : {
     551            4 :     if (!RecoveryInProgress())
     552            0 :         ereport(ERROR,
     553              :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
     554              :                  errmsg("recovery is not in progress"),
     555              :                  errhint("Recovery control functions can only be executed during recovery.")));
     556              : 
     557            4 :     if (PromoteIsTriggered())
     558            0 :         ereport(ERROR,
     559              :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
     560              :                  errmsg("standby promotion is ongoing"),
     561              :                  errhint("%s cannot be executed after promotion is triggered.",
     562              :                          "pg_wal_replay_pause()")));
     563              : 
     564            4 :     SetRecoveryPause(true);
     565              : 
     566              :     /* wake up the recovery process so that it can process the pause request */
     567            4 :     WakeupRecovery();
     568              : 
     569            4 :     PG_RETURN_VOID();
     570              : }
     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            3 : pg_wal_replay_resume(PG_FUNCTION_ARGS)
     580              : {
     581            3 :     if (!RecoveryInProgress())
     582            0 :         ereport(ERROR,
     583              :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
     584              :                  errmsg("recovery is not in progress"),
     585              :                  errhint("Recovery control functions can only be executed during recovery.")));
     586              : 
     587            3 :     if (PromoteIsTriggered())
     588            0 :         ereport(ERROR,
     589              :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
     590              :                  errmsg("standby promotion is ongoing"),
     591              :                  errhint("%s cannot be executed after promotion is triggered.",
     592              :                          "pg_wal_replay_resume()")));
     593              : 
     594            3 :     SetRecoveryPause(false);
     595              : 
     596            3 :     PG_RETURN_VOID();
     597              : }
     598              : 
     599              : /*
     600              :  * pg_is_wal_replay_paused
     601              :  */
     602              : Datum
     603            1 : pg_is_wal_replay_paused(PG_FUNCTION_ARGS)
     604              : {
     605            1 :     if (!RecoveryInProgress())
     606            0 :         ereport(ERROR,
     607              :                 (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              : 
     611            1 :     PG_RETURN_BOOL(GetRecoveryPauseState() != RECOVERY_NOT_PAUSED);
     612              : }
     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            5 : pg_get_wal_replay_pause_state(PG_FUNCTION_ARGS)
     625              : {
     626              :     RecoveryPauseState state;
     627              : 
     628            5 :     if (!RecoveryInProgress())
     629            0 :         ereport(ERROR,
     630              :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
     631              :                  errmsg("recovery is not in progress"),
     632              :                  errhint("Recovery control functions can only be executed during recovery.")));
     633              : 
     634            5 :     state = GetRecoveryPauseState();
     635              : 
     636              :     /* get the recovery pause state */
     637            5 :     PG_RETURN_TEXT_P(cstring_to_text(GetRecoveryPauseStateString(state)));
     638              : }
     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            0 : pg_last_xact_replay_timestamp(PG_FUNCTION_ARGS)
     648              : {
     649              :     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              : }
     657              : 
     658              : /*
     659              :  * Returns bool with current recovery mode, a global state.
     660              :  */
     661              : Datum
     662         1065 : pg_is_in_recovery(PG_FUNCTION_ARGS)
     663              : {
     664         1065 :     PG_RETURN_BOOL(RecoveryInProgress());
     665              : }
     666              : 
     667              : /*
     668              :  * Compute the difference in bytes between two WAL locations.
     669              :  */
     670              : Datum
     671            3 : pg_wal_lsn_diff(PG_FUNCTION_ARGS)
     672              : {
     673              :     Datum       result;
     674              : 
     675            3 :     result = DirectFunctionCall2(pg_lsn_mi,
     676              :                                  PG_GETARG_DATUM(0),
     677              :                                  PG_GETARG_DATUM(1));
     678              : 
     679            3 :     PG_RETURN_DATUM(result);
     680              : }
     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            3 : pg_promote(PG_FUNCTION_ARGS)
     690              : {
     691            3 :     bool        wait = PG_GETARG_BOOL(0);
     692            3 :     int         wait_seconds = PG_GETARG_INT32(1);
     693              :     FILE       *promote_file;
     694              :     int         i;
     695              : 
     696            3 :     if (!RecoveryInProgress())
     697            0 :         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            3 :     if (wait_seconds <= 0)
     703            0 :         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            3 :     promote_file = AllocateFile(PROMOTE_SIGNAL_FILE, "w");
     709            3 :     if (!promote_file)
     710            0 :         ereport(ERROR,
     711              :                 (errcode_for_file_access(),
     712              :                  errmsg("could not create file \"%s\": %m",
     713              :                         PROMOTE_SIGNAL_FILE)));
     714              : 
     715            3 :     if (FreeFile(promote_file))
     716            0 :         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            3 :     if (kill(PostmasterPid, SIGUSR1) != 0)
     723              :     {
     724            0 :         (void) unlink(PROMOTE_SIGNAL_FILE);
     725            0 :         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            3 :     if (!wait)
     732            1 :         PG_RETURN_BOOL(true);
     733              : 
     734              :     /* wait for the amount of time wanted until promotion */
     735              : #define WAITS_PER_SECOND 10
     736           22 :     for (i = 0; i < WAITS_PER_SECOND * wait_seconds; i++)
     737              :     {
     738              :         int         rc;
     739              : 
     740           22 :         ResetLatch(MyLatch);
     741              : 
     742           22 :         if (!RecoveryInProgress())
     743            2 :             PG_RETURN_BOOL(true);
     744              : 
     745           20 :         CHECK_FOR_INTERRUPTS();
     746              : 
     747           20 :         rc = WaitLatch(MyLatch,
     748              :                        WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
     749              :                        1000L / WAITS_PER_SECOND,
     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           20 :         if (rc & WL_POSTMASTER_DEATH)
     757            0 :             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            0 :     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            0 :     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            5 : pg_stat_get_recovery(PG_FUNCTION_ARGS)
     779              : {
     780              :     TupleDesc   tupdesc;
     781              :     Datum      *values;
     782              :     bool       *nulls;
     783              : 
     784              :     /* Local copies of shared state */
     785              :     bool        promote_triggered;
     786              :     XLogRecPtr  last_replayed_read_lsn;
     787              :     XLogRecPtr  last_replayed_end_lsn;
     788              :     TimeLineID  last_replayed_tli;
     789              :     XLogRecPtr  replay_end_lsn;
     790              :     TimeLineID  replay_end_tli;
     791              :     TimestampTz recovery_last_xact_time;
     792              :     TimestampTz current_chunk_start_time;
     793              :     RecoveryPauseState pause_state;
     794              : 
     795            5 :     if (!RecoveryInProgress())
     796            4 :         PG_RETURN_NULL();
     797              : 
     798            1 :     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            1 :     SpinLockAcquire(&XLogRecoveryCtl->info_lck);
     803            1 :     promote_triggered = XLogRecoveryCtl->SharedPromoteIsTriggered;
     804            1 :     last_replayed_read_lsn = XLogRecoveryCtl->lastReplayedReadRecPtr;
     805            1 :     last_replayed_end_lsn = XLogRecoveryCtl->lastReplayedEndRecPtr;
     806            1 :     last_replayed_tli = XLogRecoveryCtl->lastReplayedTLI;
     807            1 :     replay_end_lsn = XLogRecoveryCtl->replayEndRecPtr;
     808            1 :     replay_end_tli = XLogRecoveryCtl->replayEndTLI;
     809            1 :     recovery_last_xact_time = XLogRecoveryCtl->recoveryLastXTime;
     810            1 :     current_chunk_start_time = XLogRecoveryCtl->currentChunkStartTime;
     811            1 :     pause_state = XLogRecoveryCtl->recoveryPauseState;
     812            1 :     SpinLockRelease(&XLogRecoveryCtl->info_lck);
     813              : 
     814            1 :     if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
     815            0 :         elog(ERROR, "return type must be a row type");
     816              : 
     817            1 :     values = palloc0_array(Datum, tupdesc->natts);
     818            1 :     nulls = palloc0_array(bool, tupdesc->natts);
     819              : 
     820            1 :     values[0] = BoolGetDatum(promote_triggered);
     821              : 
     822            1 :     if (XLogRecPtrIsValid(last_replayed_read_lsn))
     823            1 :         values[1] = LSNGetDatum(last_replayed_read_lsn);
     824              :     else
     825            0 :         nulls[1] = true;
     826              : 
     827            1 :     if (XLogRecPtrIsValid(last_replayed_end_lsn))
     828            1 :         values[2] = LSNGetDatum(last_replayed_end_lsn);
     829              :     else
     830            0 :         nulls[2] = true;
     831              : 
     832            1 :     if (XLogRecPtrIsValid(last_replayed_end_lsn))
     833            1 :         values[3] = Int32GetDatum(last_replayed_tli);
     834              :     else
     835            0 :         nulls[3] = true;
     836              : 
     837            1 :     if (XLogRecPtrIsValid(replay_end_lsn))
     838            1 :         values[4] = LSNGetDatum(replay_end_lsn);
     839              :     else
     840            0 :         nulls[4] = true;
     841              : 
     842            1 :     if (XLogRecPtrIsValid(replay_end_lsn))
     843            1 :         values[5] = Int32GetDatum(replay_end_tli);
     844              :     else
     845            0 :         nulls[5] = true;
     846              : 
     847            1 :     if (recovery_last_xact_time != 0)
     848            1 :         values[6] = TimestampTzGetDatum(recovery_last_xact_time);
     849              :     else
     850            0 :         nulls[6] = true;
     851              : 
     852            1 :     if (current_chunk_start_time != 0)
     853            1 :         values[7] = TimestampTzGetDatum(current_chunk_start_time);
     854              :     else
     855            0 :         nulls[7] = true;
     856              : 
     857            1 :     values[8] = CStringGetTextDatum(GetRecoveryPauseStateString(pause_state));
     858              : 
     859            1 :     PG_RETURN_DATUM(HeapTupleGetDatum(heap_form_tuple(tupdesc, values, nulls)));
     860              : }
        

Generated by: LCOV version 2.0-1