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