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