LCOV - code coverage report
Current view: top level - src/backend/access/transam - xlogfuncs.c (source / functions) Hit Total Coverage
Test: PostgreSQL 13beta1 Lines: 67 205 32.7 %
Date: 2020-05-25 05:06:35 Functions: 11 21 52.4 %
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-2020, 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.h"
      23             : #include "access/xlog_internal.h"
      24             : #include "access/xlogutils.h"
      25             : #include "catalog/pg_type.h"
      26             : #include "funcapi.h"
      27             : #include "miscadmin.h"
      28             : #include "pgstat.h"
      29             : #include "replication/walreceiver.h"
      30             : #include "storage/fd.h"
      31             : #include "storage/ipc.h"
      32             : #include "storage/smgr.h"
      33             : #include "utils/builtins.h"
      34             : #include "utils/guc.h"
      35             : #include "utils/memutils.h"
      36             : #include "utils/numeric.h"
      37             : #include "utils/pg_lsn.h"
      38             : #include "utils/timestamp.h"
      39             : #include "utils/tuplestore.h"
      40             : 
      41             : /*
      42             :  * Store label file and tablespace map during non-exclusive backups.
      43             :  */
      44             : static StringInfo label_file;
      45             : static StringInfo tblspc_map_file;
      46             : 
      47             : /*
      48             :  * pg_start_backup: set up for taking an on-line backup dump
      49             :  *
      50             :  * Essentially what this does is to create a backup label file in $PGDATA,
      51             :  * where it will be archived as part of the backup dump.  The label file
      52             :  * contains the user-supplied label string (typically this would be used
      53             :  * to tell where the backup dump will be stored) and the starting time and
      54             :  * starting WAL location for the dump.
      55             :  *
      56             :  * Permission checking for this function is managed through the normal
      57             :  * GRANT system.
      58             :  */
      59             : Datum
      60           2 : pg_start_backup(PG_FUNCTION_ARGS)
      61             : {
      62           2 :     text       *backupid = PG_GETARG_TEXT_PP(0);
      63           2 :     bool        fast = PG_GETARG_BOOL(1);
      64           2 :     bool        exclusive = PG_GETARG_BOOL(2);
      65             :     char       *backupidstr;
      66             :     XLogRecPtr  startpoint;
      67           2 :     SessionBackupState status = get_backup_status();
      68             : 
      69           2 :     backupidstr = text_to_cstring(backupid);
      70             : 
      71           2 :     if (status == SESSION_BACKUP_NON_EXCLUSIVE)
      72           0 :         ereport(ERROR,
      73             :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
      74             :                  errmsg("a backup is already in progress in this session")));
      75             : 
      76           2 :     if (exclusive)
      77             :     {
      78           2 :         startpoint = do_pg_start_backup(backupidstr, fast, NULL, NULL,
      79             :                                         NULL, NULL, false, true);
      80             :     }
      81             :     else
      82             :     {
      83             :         MemoryContext oldcontext;
      84             : 
      85             :         /*
      86             :          * Label file and tablespace map file need to be long-lived, since
      87             :          * they are read in pg_stop_backup.
      88             :          */
      89           0 :         oldcontext = MemoryContextSwitchTo(TopMemoryContext);
      90           0 :         label_file = makeStringInfo();
      91           0 :         tblspc_map_file = makeStringInfo();
      92           0 :         MemoryContextSwitchTo(oldcontext);
      93             : 
      94           0 :         register_persistent_abort_backup_handler();
      95             : 
      96           0 :         startpoint = do_pg_start_backup(backupidstr, fast, NULL, label_file,
      97             :                                         NULL, tblspc_map_file, false, true);
      98             :     }
      99             : 
     100           2 :     PG_RETURN_LSN(startpoint);
     101             : }
     102             : 
     103             : /*
     104             :  * pg_stop_backup: finish taking an on-line backup dump
     105             :  *
     106             :  * We write an end-of-backup WAL record, and remove the backup label file
     107             :  * created by pg_start_backup, creating a backup history file in pg_wal
     108             :  * instead (whence it will immediately be archived). The backup history file
     109             :  * contains the same info found in the label file, plus the backup-end time
     110             :  * and WAL location. Before 9.0, the backup-end time was read from the backup
     111             :  * history file at the beginning of archive recovery, but we now use the WAL
     112             :  * record for that and the file is for informational and debug purposes only.
     113             :  *
     114             :  * Note: different from CancelBackup which just cancels online backup mode.
     115             :  *
     116             :  * Note: this version is only called to stop an exclusive backup. The function
     117             :  *       pg_stop_backup_v2 (overloaded as pg_stop_backup in SQL) is called to
     118             :  *       stop non-exclusive backups.
     119             :  *
     120             :  * Permission checking for this function is managed through the normal
     121             :  * GRANT system.
     122             :  */
     123             : Datum
     124           2 : pg_stop_backup(PG_FUNCTION_ARGS)
     125             : {
     126             :     XLogRecPtr  stoppoint;
     127           2 :     SessionBackupState status = get_backup_status();
     128             : 
     129           2 :     if (status == SESSION_BACKUP_NON_EXCLUSIVE)
     130           0 :         ereport(ERROR,
     131             :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
     132             :                  errmsg("non-exclusive backup in progress"),
     133             :                  errhint("Did you mean to use pg_stop_backup('f')?")));
     134             : 
     135             :     /*
     136             :      * Exclusive backups were typically started in a different connection, so
     137             :      * don't try to verify that status of backup is set to
     138             :      * SESSION_BACKUP_EXCLUSIVE in this function. Actual verification that an
     139             :      * exclusive backup is in fact running is handled inside
     140             :      * do_pg_stop_backup.
     141             :      */
     142           2 :     stoppoint = do_pg_stop_backup(NULL, true, NULL);
     143             : 
     144           2 :     PG_RETURN_LSN(stoppoint);
     145             : }
     146             : 
     147             : 
     148             : /*
     149             :  * pg_stop_backup_v2: finish taking exclusive or nonexclusive on-line backup.
     150             :  *
     151             :  * Works the same as pg_stop_backup, except for non-exclusive backups it returns
     152             :  * the backup label and tablespace map files as text fields in as part of the
     153             :  * resultset.
     154             :  *
     155             :  * The first parameter (variable 'exclusive') allows the user to tell us if
     156             :  * this is an exclusive or a non-exclusive backup.
     157             :  *
     158             :  * The second parameter (variable 'waitforarchive'), which is optional,
     159             :  * allows the user to choose if they want to wait for the WAL to be archived
     160             :  * or if we should just return as soon as the WAL record is written.
     161             :  *
     162             :  * Permission checking for this function is managed through the normal
     163             :  * GRANT system.
     164             :  */
     165             : Datum
     166           0 : pg_stop_backup_v2(PG_FUNCTION_ARGS)
     167             : {
     168           0 :     ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
     169             :     TupleDesc   tupdesc;
     170             :     Tuplestorestate *tupstore;
     171             :     MemoryContext per_query_ctx;
     172             :     MemoryContext oldcontext;
     173             :     Datum       values[3];
     174             :     bool        nulls[3];
     175             : 
     176           0 :     bool        exclusive = PG_GETARG_BOOL(0);
     177           0 :     bool        waitforarchive = PG_GETARG_BOOL(1);
     178             :     XLogRecPtr  stoppoint;
     179           0 :     SessionBackupState status = get_backup_status();
     180             : 
     181             :     /* check to see if caller supports us returning a tuplestore */
     182           0 :     if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
     183           0 :         ereport(ERROR,
     184             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     185             :                  errmsg("set-valued function called in context that cannot accept a set")));
     186           0 :     if (!(rsinfo->allowedModes & SFRM_Materialize))
     187           0 :         ereport(ERROR,
     188             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     189             :                  errmsg("materialize mode required, but it is not allowed in this context")));
     190             : 
     191             :     /* Build a tuple descriptor for our result type */
     192           0 :     if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
     193           0 :         elog(ERROR, "return type must be a row type");
     194             : 
     195           0 :     per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
     196           0 :     oldcontext = MemoryContextSwitchTo(per_query_ctx);
     197             : 
     198           0 :     tupstore = tuplestore_begin_heap(true, false, work_mem);
     199           0 :     rsinfo->returnMode = SFRM_Materialize;
     200           0 :     rsinfo->setResult = tupstore;
     201           0 :     rsinfo->setDesc = tupdesc;
     202             : 
     203           0 :     MemoryContextSwitchTo(oldcontext);
     204             : 
     205           0 :     MemSet(values, 0, sizeof(values));
     206           0 :     MemSet(nulls, 0, sizeof(nulls));
     207             : 
     208           0 :     if (exclusive)
     209             :     {
     210           0 :         if (status == SESSION_BACKUP_NON_EXCLUSIVE)
     211           0 :             ereport(ERROR,
     212             :                     (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
     213             :                      errmsg("non-exclusive backup in progress"),
     214             :                      errhint("Did you mean to use pg_stop_backup('f')?")));
     215             : 
     216             :         /*
     217             :          * Stop the exclusive backup, and since we're in an exclusive backup
     218             :          * return NULL for both backup_label and tablespace_map.
     219             :          */
     220           0 :         stoppoint = do_pg_stop_backup(NULL, waitforarchive, NULL);
     221             : 
     222           0 :         nulls[1] = true;
     223           0 :         nulls[2] = true;
     224             :     }
     225             :     else
     226             :     {
     227           0 :         if (status != SESSION_BACKUP_NON_EXCLUSIVE)
     228           0 :             ereport(ERROR,
     229             :                     (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
     230             :                      errmsg("non-exclusive backup is not in progress"),
     231             :                      errhint("Did you mean to use pg_stop_backup('t')?")));
     232             : 
     233             :         /*
     234             :          * Stop the non-exclusive backup. Return a copy of the backup label
     235             :          * and tablespace map so they can be written to disk by the caller.
     236             :          */
     237           0 :         stoppoint = do_pg_stop_backup(label_file->data, waitforarchive, NULL);
     238             : 
     239           0 :         values[1] = CStringGetTextDatum(label_file->data);
     240           0 :         values[2] = CStringGetTextDatum(tblspc_map_file->data);
     241             : 
     242             :         /* Free structures allocated in TopMemoryContext */
     243           0 :         pfree(label_file->data);
     244           0 :         pfree(label_file);
     245           0 :         label_file = NULL;
     246           0 :         pfree(tblspc_map_file->data);
     247           0 :         pfree(tblspc_map_file);
     248           0 :         tblspc_map_file = NULL;
     249             :     }
     250             : 
     251             :     /* Stoppoint is included on both exclusive and nonexclusive backups */
     252           0 :     values[0] = LSNGetDatum(stoppoint);
     253             : 
     254           0 :     tuplestore_putvalues(tupstore, tupdesc, values, nulls);
     255             :     tuplestore_donestoring(tupstore);
     256             : 
     257           0 :     return (Datum) 0;
     258             : }
     259             : 
     260             : /*
     261             :  * pg_switch_wal: switch to next xlog file
     262             :  *
     263             :  * Permission checking for this function is managed through the normal
     264             :  * GRANT system.
     265             :  */
     266             : Datum
     267          66 : pg_switch_wal(PG_FUNCTION_ARGS)
     268             : {
     269             :     XLogRecPtr  switchpoint;
     270             : 
     271          66 :     if (RecoveryInProgress())
     272           0 :         ereport(ERROR,
     273             :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
     274             :                  errmsg("recovery is in progress"),
     275             :                  errhint("WAL control functions cannot be executed during recovery.")));
     276             : 
     277          66 :     switchpoint = RequestXLogSwitch(false);
     278             : 
     279             :     /*
     280             :      * As a convenience, return the WAL location of the switch record
     281             :      */
     282          66 :     PG_RETURN_LSN(switchpoint);
     283             : }
     284             : 
     285             : /*
     286             :  * pg_create_restore_point: a named point for restore
     287             :  *
     288             :  * Permission checking for this function is managed through the normal
     289             :  * GRANT system.
     290             :  */
     291             : Datum
     292           2 : pg_create_restore_point(PG_FUNCTION_ARGS)
     293             : {
     294           2 :     text       *restore_name = PG_GETARG_TEXT_PP(0);
     295             :     char       *restore_name_str;
     296             :     XLogRecPtr  restorepoint;
     297             : 
     298           2 :     if (RecoveryInProgress())
     299           0 :         ereport(ERROR,
     300             :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
     301             :                  errmsg("recovery is in progress"),
     302             :                  errhint("WAL control functions cannot be executed during recovery.")));
     303             : 
     304           2 :     if (!XLogIsNeeded())
     305           0 :         ereport(ERROR,
     306             :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
     307             :                  errmsg("WAL level not sufficient for creating a restore point"),
     308             :                  errhint("wal_level must be set to \"replica\" or \"logical\" at server start.")));
     309             : 
     310           2 :     restore_name_str = text_to_cstring(restore_name);
     311             : 
     312           2 :     if (strlen(restore_name_str) >= MAXFNAMELEN)
     313           0 :         ereport(ERROR,
     314             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     315             :                  errmsg("value too long for restore point (maximum %d characters)", MAXFNAMELEN - 1)));
     316             : 
     317           2 :     restorepoint = XLogRestorePoint(restore_name_str);
     318             : 
     319             :     /*
     320             :      * As a convenience, return the WAL location of the restore point record
     321             :      */
     322           2 :     PG_RETURN_LSN(restorepoint);
     323             : }
     324             : 
     325             : /*
     326             :  * Report the current WAL write location (same format as pg_start_backup etc)
     327             :  *
     328             :  * This is useful for determining how much of WAL is visible to an external
     329             :  * archiving process.  Note that the data before this point is written out
     330             :  * to the kernel, but is not necessarily synced to disk.
     331             :  */
     332             : Datum
     333         218 : pg_current_wal_lsn(PG_FUNCTION_ARGS)
     334             : {
     335             :     XLogRecPtr  current_recptr;
     336             : 
     337         218 :     if (RecoveryInProgress())
     338           0 :         ereport(ERROR,
     339             :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
     340             :                  errmsg("recovery is in progress"),
     341             :                  errhint("WAL control functions cannot be executed during recovery.")));
     342             : 
     343         218 :     current_recptr = GetXLogWriteRecPtr();
     344             : 
     345         218 :     PG_RETURN_LSN(current_recptr);
     346             : }
     347             : 
     348             : /*
     349             :  * Report the current WAL insert location (same format as pg_start_backup etc)
     350             :  *
     351             :  * This function is mostly for debugging purposes.
     352             :  */
     353             : Datum
     354          38 : pg_current_wal_insert_lsn(PG_FUNCTION_ARGS)
     355             : {
     356             :     XLogRecPtr  current_recptr;
     357             : 
     358          38 :     if (RecoveryInProgress())
     359           0 :         ereport(ERROR,
     360             :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
     361             :                  errmsg("recovery is in progress"),
     362             :                  errhint("WAL control functions cannot be executed during recovery.")));
     363             : 
     364          38 :     current_recptr = GetXLogInsertRecPtr();
     365             : 
     366          38 :     PG_RETURN_LSN(current_recptr);
     367             : }
     368             : 
     369             : /*
     370             :  * Report the current WAL flush location (same format as pg_start_backup etc)
     371             :  *
     372             :  * This function is mostly for debugging purposes.
     373             :  */
     374             : Datum
     375           0 : pg_current_wal_flush_lsn(PG_FUNCTION_ARGS)
     376             : {
     377             :     XLogRecPtr  current_recptr;
     378             : 
     379           0 :     if (RecoveryInProgress())
     380           0 :         ereport(ERROR,
     381             :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
     382             :                  errmsg("recovery is in progress"),
     383             :                  errhint("WAL control functions cannot be executed during recovery.")));
     384             : 
     385           0 :     current_recptr = GetFlushRecPtr();
     386             : 
     387           0 :     PG_RETURN_LSN(current_recptr);
     388             : }
     389             : 
     390             : /*
     391             :  * Report the last WAL receive location (same format as pg_start_backup etc)
     392             :  *
     393             :  * This is useful for determining how much of WAL is guaranteed to be received
     394             :  * and synced to disk by walreceiver.
     395             :  */
     396             : Datum
     397           0 : pg_last_wal_receive_lsn(PG_FUNCTION_ARGS)
     398             : {
     399             :     XLogRecPtr  recptr;
     400             : 
     401           0 :     recptr = GetWalRcvFlushRecPtr(NULL, NULL);
     402             : 
     403           0 :     if (recptr == 0)
     404           0 :         PG_RETURN_NULL();
     405             : 
     406           0 :     PG_RETURN_LSN(recptr);
     407             : }
     408             : 
     409             : /*
     410             :  * Report the last WAL replay location (same format as pg_start_backup etc)
     411             :  *
     412             :  * This is useful for determining how much of WAL is visible to read-only
     413             :  * connections during recovery.
     414             :  */
     415             : Datum
     416          64 : pg_last_wal_replay_lsn(PG_FUNCTION_ARGS)
     417             : {
     418             :     XLogRecPtr  recptr;
     419             : 
     420          64 :     recptr = GetXLogReplayRecPtr(NULL);
     421             : 
     422          64 :     if (recptr == 0)
     423           0 :         PG_RETURN_NULL();
     424             : 
     425          64 :     PG_RETURN_LSN(recptr);
     426             : }
     427             : 
     428             : /*
     429             :  * Compute an xlog file name and decimal byte offset given a WAL location,
     430             :  * such as is returned by pg_stop_backup() or pg_switch_wal().
     431             :  *
     432             :  * Note that a location exactly at a segment boundary is taken to be in
     433             :  * the previous segment.  This is usually the right thing, since the
     434             :  * expected usage is to determine which xlog file(s) are ready to archive.
     435             :  */
     436             : Datum
     437           0 : pg_walfile_name_offset(PG_FUNCTION_ARGS)
     438             : {
     439             :     XLogSegNo   xlogsegno;
     440             :     uint32      xrecoff;
     441           0 :     XLogRecPtr  locationpoint = PG_GETARG_LSN(0);
     442             :     char        xlogfilename[MAXFNAMELEN];
     443             :     Datum       values[2];
     444             :     bool        isnull[2];
     445             :     TupleDesc   resultTupleDesc;
     446             :     HeapTuple   resultHeapTuple;
     447             :     Datum       result;
     448             : 
     449           0 :     if (RecoveryInProgress())
     450           0 :         ereport(ERROR,
     451             :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
     452             :                  errmsg("recovery is in progress"),
     453             :                  errhint("%s cannot be executed during recovery.",
     454             :                          "pg_walfile_name_offset()")));
     455             : 
     456             :     /*
     457             :      * Construct a tuple descriptor for the result row.  This must match this
     458             :      * function's pg_proc entry!
     459             :      */
     460           0 :     resultTupleDesc = CreateTemplateTupleDesc(2);
     461           0 :     TupleDescInitEntry(resultTupleDesc, (AttrNumber) 1, "file_name",
     462             :                        TEXTOID, -1, 0);
     463           0 :     TupleDescInitEntry(resultTupleDesc, (AttrNumber) 2, "file_offset",
     464             :                        INT4OID, -1, 0);
     465             : 
     466           0 :     resultTupleDesc = BlessTupleDesc(resultTupleDesc);
     467             : 
     468             :     /*
     469             :      * xlogfilename
     470             :      */
     471           0 :     XLByteToPrevSeg(locationpoint, xlogsegno, wal_segment_size);
     472           0 :     XLogFileName(xlogfilename, ThisTimeLineID, xlogsegno, wal_segment_size);
     473             : 
     474           0 :     values[0] = CStringGetTextDatum(xlogfilename);
     475           0 :     isnull[0] = false;
     476             : 
     477             :     /*
     478             :      * offset
     479             :      */
     480           0 :     xrecoff = XLogSegmentOffset(locationpoint, wal_segment_size);
     481             : 
     482           0 :     values[1] = UInt32GetDatum(xrecoff);
     483           0 :     isnull[1] = false;
     484             : 
     485             :     /*
     486             :      * Tuple jam: Having first prepared your Datums, then squash together
     487             :      */
     488           0 :     resultHeapTuple = heap_form_tuple(resultTupleDesc, values, isnull);
     489             : 
     490           0 :     result = HeapTupleGetDatum(resultHeapTuple);
     491             : 
     492           0 :     PG_RETURN_DATUM(result);
     493             : }
     494             : 
     495             : /*
     496             :  * Compute an xlog file name given a WAL location,
     497             :  * such as is returned by pg_stop_backup() or pg_switch_wal().
     498             :  */
     499             : Datum
     500           4 : pg_walfile_name(PG_FUNCTION_ARGS)
     501             : {
     502             :     XLogSegNo   xlogsegno;
     503           4 :     XLogRecPtr  locationpoint = PG_GETARG_LSN(0);
     504             :     char        xlogfilename[MAXFNAMELEN];
     505             : 
     506           4 :     if (RecoveryInProgress())
     507           0 :         ereport(ERROR,
     508             :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
     509             :                  errmsg("recovery is in progress"),
     510             :                  errhint("%s cannot be executed during recovery.",
     511             :                          "pg_walfile_name()")));
     512             : 
     513           4 :     XLByteToPrevSeg(locationpoint, xlogsegno, wal_segment_size);
     514           4 :     XLogFileName(xlogfilename, ThisTimeLineID, xlogsegno, wal_segment_size);
     515             : 
     516           4 :     PG_RETURN_TEXT_P(cstring_to_text(xlogfilename));
     517             : }
     518             : 
     519             : /*
     520             :  * pg_wal_replay_pause - pause recovery now
     521             :  *
     522             :  * Permission checking for this function is managed through the normal
     523             :  * GRANT system.
     524             :  */
     525             : Datum
     526           0 : pg_wal_replay_pause(PG_FUNCTION_ARGS)
     527             : {
     528           0 :     if (!RecoveryInProgress())
     529           0 :         ereport(ERROR,
     530             :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
     531             :                  errmsg("recovery is not in progress"),
     532             :                  errhint("Recovery control functions can only be executed during recovery.")));
     533             : 
     534           0 :     if (PromoteIsTriggered())
     535           0 :         ereport(ERROR,
     536             :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
     537             :                  errmsg("standby promotion is ongoing"),
     538             :                  errhint("%s cannot be executed after promotion is triggered.",
     539             :                          "pg_wal_replay_pause()")));
     540             : 
     541           0 :     SetRecoveryPause(true);
     542             : 
     543           0 :     PG_RETURN_VOID();
     544             : }
     545             : 
     546             : /*
     547             :  * pg_wal_replay_resume - resume recovery now
     548             :  *
     549             :  * Permission checking for this function is managed through the normal
     550             :  * GRANT system.
     551             :  */
     552             : Datum
     553           0 : pg_wal_replay_resume(PG_FUNCTION_ARGS)
     554             : {
     555           0 :     if (!RecoveryInProgress())
     556           0 :         ereport(ERROR,
     557             :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
     558             :                  errmsg("recovery is not in progress"),
     559             :                  errhint("Recovery control functions can only be executed during recovery.")));
     560             : 
     561           0 :     if (PromoteIsTriggered())
     562           0 :         ereport(ERROR,
     563             :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
     564             :                  errmsg("standby promotion is ongoing"),
     565             :                  errhint("%s cannot be executed after promotion is triggered.",
     566             :                          "pg_wal_replay_resume()")));
     567             : 
     568           0 :     SetRecoveryPause(false);
     569             : 
     570           0 :     PG_RETURN_VOID();
     571             : }
     572             : 
     573             : /*
     574             :  * pg_is_wal_replay_paused
     575             :  */
     576             : Datum
     577           0 : pg_is_wal_replay_paused(PG_FUNCTION_ARGS)
     578             : {
     579           0 :     if (!RecoveryInProgress())
     580           0 :         ereport(ERROR,
     581             :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
     582             :                  errmsg("recovery is not in progress"),
     583             :                  errhint("Recovery control functions can only be executed during recovery.")));
     584             : 
     585           0 :     PG_RETURN_BOOL(RecoveryIsPaused());
     586             : }
     587             : 
     588             : /*
     589             :  * Returns timestamp of latest processed commit/abort record.
     590             :  *
     591             :  * When the server has been started normally without recovery the function
     592             :  * returns NULL.
     593             :  */
     594             : Datum
     595           0 : pg_last_xact_replay_timestamp(PG_FUNCTION_ARGS)
     596             : {
     597             :     TimestampTz xtime;
     598             : 
     599           0 :     xtime = GetLatestXTime();
     600           0 :     if (xtime == 0)
     601           0 :         PG_RETURN_NULL();
     602             : 
     603           0 :     PG_RETURN_TIMESTAMPTZ(xtime);
     604             : }
     605             : 
     606             : /*
     607             :  * Returns bool with current recovery mode, a global state.
     608             :  */
     609             : Datum
     610         256 : pg_is_in_recovery(PG_FUNCTION_ARGS)
     611             : {
     612         256 :     PG_RETURN_BOOL(RecoveryInProgress());
     613             : }
     614             : 
     615             : /*
     616             :  * Compute the difference in bytes between two WAL locations.
     617             :  */
     618             : Datum
     619           6 : pg_wal_lsn_diff(PG_FUNCTION_ARGS)
     620             : {
     621             :     Datum       result;
     622             : 
     623           6 :     result = DirectFunctionCall2(pg_lsn_mi,
     624             :                                  PG_GETARG_DATUM(0),
     625             :                                  PG_GETARG_DATUM(1));
     626             : 
     627           6 :     PG_RETURN_NUMERIC(result);
     628             : }
     629             : 
     630             : /*
     631             :  * Returns bool with current on-line backup mode, a global state.
     632             :  */
     633             : Datum
     634           0 : pg_is_in_backup(PG_FUNCTION_ARGS)
     635             : {
     636           0 :     PG_RETURN_BOOL(BackupInProgress());
     637             : }
     638             : 
     639             : /*
     640             :  * Returns start time of an online exclusive backup.
     641             :  *
     642             :  * When there's no exclusive backup in progress, the function
     643             :  * returns NULL.
     644             :  */
     645             : Datum
     646           0 : pg_backup_start_time(PG_FUNCTION_ARGS)
     647             : {
     648             :     Datum       xtime;
     649             :     FILE       *lfp;
     650             :     char        fline[MAXPGPATH];
     651             :     char        backup_start_time[30];
     652             : 
     653             :     /*
     654             :      * See if label file is present
     655             :      */
     656           0 :     lfp = AllocateFile(BACKUP_LABEL_FILE, "r");
     657           0 :     if (lfp == NULL)
     658             :     {
     659           0 :         if (errno != ENOENT)
     660           0 :             ereport(ERROR,
     661             :                     (errcode_for_file_access(),
     662             :                      errmsg("could not read file \"%s\": %m",
     663             :                             BACKUP_LABEL_FILE)));
     664           0 :         PG_RETURN_NULL();
     665             :     }
     666             : 
     667             :     /*
     668             :      * Parse the file to find the START TIME line.
     669             :      */
     670           0 :     backup_start_time[0] = '\0';
     671           0 :     while (fgets(fline, sizeof(fline), lfp) != NULL)
     672             :     {
     673           0 :         if (sscanf(fline, "START TIME: %25[^\n]\n", backup_start_time) == 1)
     674           0 :             break;
     675             :     }
     676             : 
     677             :     /* Check for a read error. */
     678           0 :     if (ferror(lfp))
     679           0 :         ereport(ERROR,
     680             :                 (errcode_for_file_access(),
     681             :                  errmsg("could not read file \"%s\": %m", BACKUP_LABEL_FILE)));
     682             : 
     683             :     /* Close the backup label file. */
     684           0 :     if (FreeFile(lfp))
     685           0 :         ereport(ERROR,
     686             :                 (errcode_for_file_access(),
     687             :                  errmsg("could not close file \"%s\": %m", BACKUP_LABEL_FILE)));
     688             : 
     689           0 :     if (strlen(backup_start_time) == 0)
     690           0 :         ereport(ERROR,
     691             :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
     692             :                  errmsg("invalid data in file \"%s\"", BACKUP_LABEL_FILE)));
     693             : 
     694             :     /*
     695             :      * Convert the time string read from file to TimestampTz form.
     696             :      */
     697           0 :     xtime = DirectFunctionCall3(timestamptz_in,
     698             :                                 CStringGetDatum(backup_start_time),
     699             :                                 ObjectIdGetDatum(InvalidOid),
     700             :                                 Int32GetDatum(-1));
     701             : 
     702           0 :     PG_RETURN_DATUM(xtime);
     703             : }
     704             : 
     705             : /*
     706             :  * Promotes a standby server.
     707             :  *
     708             :  * A result of "true" means that promotion has been completed if "wait" is
     709             :  * "true", or initiated if "wait" is false.
     710             :  */
     711             : Datum
     712           2 : pg_promote(PG_FUNCTION_ARGS)
     713             : {
     714           2 :     bool        wait = PG_GETARG_BOOL(0);
     715           2 :     int         wait_seconds = PG_GETARG_INT32(1);
     716             :     FILE       *promote_file;
     717             :     int         i;
     718             : 
     719           2 :     if (!RecoveryInProgress())
     720           0 :         ereport(ERROR,
     721             :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
     722             :                  errmsg("recovery is not in progress"),
     723             :                  errhint("Recovery control functions can only be executed during recovery.")));
     724             : 
     725           2 :     if (wait_seconds <= 0)
     726           0 :         ereport(ERROR,
     727             :                 (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
     728             :                  errmsg("\"wait_seconds\" must not be negative or zero")));
     729             : 
     730             :     /* create the promote signal file */
     731           2 :     promote_file = AllocateFile(PROMOTE_SIGNAL_FILE, "w");
     732           2 :     if (!promote_file)
     733           0 :         ereport(ERROR,
     734             :                 (errcode_for_file_access(),
     735             :                  errmsg("could not create file \"%s\": %m",
     736             :                         PROMOTE_SIGNAL_FILE)));
     737             : 
     738           2 :     if (FreeFile(promote_file))
     739           0 :         ereport(ERROR,
     740             :                 (errcode_for_file_access(),
     741             :                  errmsg("could not write file \"%s\": %m",
     742             :                         PROMOTE_SIGNAL_FILE)));
     743             : 
     744             :     /* signal the postmaster */
     745           2 :     if (kill(PostmasterPid, SIGUSR1) != 0)
     746             :     {
     747           0 :         ereport(WARNING,
     748             :                 (errmsg("failed to send signal to postmaster: %m")));
     749           0 :         (void) unlink(PROMOTE_SIGNAL_FILE);
     750           0 :         PG_RETURN_BOOL(false);
     751             :     }
     752             : 
     753             :     /* return immediately if waiting was not requested */
     754           2 :     if (!wait)
     755           0 :         PG_RETURN_BOOL(true);
     756             : 
     757             :     /* wait for the amount of time wanted until promotion */
     758             : #define WAITS_PER_SECOND 10
     759           4 :     for (i = 0; i < WAITS_PER_SECOND * wait_seconds; i++)
     760             :     {
     761             :         int         rc;
     762             : 
     763           4 :         ResetLatch(MyLatch);
     764             : 
     765           4 :         if (!RecoveryInProgress())
     766           2 :             PG_RETURN_BOOL(true);
     767             : 
     768           2 :         CHECK_FOR_INTERRUPTS();
     769             : 
     770           2 :         rc = WaitLatch(MyLatch,
     771             :                        WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
     772             :                        1000L / WAITS_PER_SECOND,
     773             :                        WAIT_EVENT_PROMOTE);
     774             : 
     775             :         /*
     776             :          * Emergency bailout if postmaster has died.  This is to avoid the
     777             :          * necessity for manual cleanup of all postmaster children.
     778             :          */
     779           2 :         if (rc & WL_POSTMASTER_DEATH)
     780           0 :             PG_RETURN_BOOL(false);
     781             :     }
     782             : 
     783           0 :     ereport(WARNING,
     784             :             (errmsg("server did not promote within %d seconds", wait_seconds)));
     785           0 :     PG_RETURN_BOOL(false);
     786             : }

Generated by: LCOV version 1.13