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