From a030bfa6e41edae8a9a68dc8cef7fc7813f69a0a Mon Sep 17 00:00:00 2001 From: Simon Riggs Date: Fri, 4 Nov 2011 09:37:17 +0000 Subject: [PATCH] Move user functions related to WAL into xlogfuncs.c --- src/backend/access/transam/Makefile | 3 +- src/backend/access/transam/xlog.c | 470 ++----------------------- src/backend/access/transam/xlogfuncs.c | 467 ++++++++++++++++++++++++ src/include/access/xlog.h | 5 + 4 files changed, 499 insertions(+), 446 deletions(-) create mode 100644 src/backend/access/transam/xlogfuncs.c diff --git a/src/backend/access/transam/Makefile b/src/backend/access/transam/Makefile index 4179433bc1..f82f10e989 100644 --- a/src/backend/access/transam/Makefile +++ b/src/backend/access/transam/Makefile @@ -12,7 +12,8 @@ subdir = src/backend/access/transam top_builddir = ../../../.. include $(top_builddir)/src/Makefile.global -OBJS = clog.o transam.o varsup.o xact.o xlog.o xlogutils.o rmgr.o slru.o subtrans.o multixact.o twophase.o twophase_rmgr.o +OBJS = clog.o transam.o varsup.o xact.o rmgr.o slru.o subtrans.o multixact.o \ + twophase.o twophase_rmgr.o xlog.o xlogfuncs.o xlogutils.o include $(top_srcdir)/src/backend/common.mk diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index 85f79b9685..0d494e2e3b 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -35,8 +35,6 @@ #include "catalog/catversion.h" #include "catalog/pg_control.h" #include "catalog/pg_database.h" -#include "catalog/pg_type.h" -#include "funcapi.h" #include "libpq/pqsignal.h" #include "miscadmin.h" #include "pgstat.h" @@ -596,10 +594,7 @@ static void exitArchiveRecovery(TimeLineID endTLI, uint32 endLogId, uint32 endLogSeg); static bool recoveryStopsHere(XLogRecord *record, bool *includeThis); static void recoveryPausesHere(void); -static bool RecoveryIsPaused(void); -static void SetRecoveryPause(bool recoveryPause); static void SetLatestXTime(TimestampTz xtime); -static TimestampTz GetLatestXTime(void); static void CheckRequiredParameterValues(void); static void XLogReportParameters(void); static void LocalSetXLogInsertAllowed(void); @@ -5762,7 +5757,7 @@ recoveryPausesHere(void) } } -static bool +bool RecoveryIsPaused(void) { /* use volatile pointer to prevent code rearrangement */ @@ -5776,7 +5771,7 @@ RecoveryIsPaused(void) return recoveryPause; } -static void +void SetRecoveryPause(bool recoveryPause) { /* use volatile pointer to prevent code rearrangement */ @@ -5787,70 +5782,6 @@ SetRecoveryPause(bool recoveryPause) SpinLockRelease(&xlogctl->info_lck); } -/* - * pg_xlog_replay_pause - pause recovery now - */ -Datum -pg_xlog_replay_pause(PG_FUNCTION_ARGS) -{ - if (!superuser()) - ereport(ERROR, - (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - (errmsg("must be superuser to control recovery")))); - - if (!RecoveryInProgress()) - ereport(ERROR, - (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), - errmsg("recovery is not in progress"), - errhint("Recovery control functions can only be executed during recovery."))); - - SetRecoveryPause(true); - - PG_RETURN_VOID(); -} - -/* - * pg_xlog_replay_resume - resume recovery now - */ -Datum -pg_xlog_replay_resume(PG_FUNCTION_ARGS) -{ - if (!superuser()) - ereport(ERROR, - (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - (errmsg("must be superuser to control recovery")))); - - if (!RecoveryInProgress()) - ereport(ERROR, - (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), - errmsg("recovery is not in progress"), - errhint("Recovery control functions can only be executed during recovery."))); - - SetRecoveryPause(false); - - PG_RETURN_VOID(); -} - -/* - * pg_is_xlog_replay_paused - */ -Datum -pg_is_xlog_replay_paused(PG_FUNCTION_ARGS) -{ - if (!superuser()) - ereport(ERROR, - (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - (errmsg("must be superuser to control recovery")))); - - if (!RecoveryInProgress()) - ereport(ERROR, - (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), - errmsg("recovery is not in progress"), - errhint("Recovery control functions can only be executed during recovery."))); - - PG_RETURN_BOOL(RecoveryIsPaused()); -} - /* * Save timestamp of latest processed commit/abort record. * @@ -5872,7 +5803,7 @@ SetLatestXTime(TimestampTz xtime) /* * Fetch timestamp of latest processed commit/abort record. */ -static TimestampTz +TimestampTz GetLatestXTime(void) { /* use volatile pointer to prevent code rearrangement */ @@ -5886,33 +5817,6 @@ GetLatestXTime(void) return xtime; } -/* - * Returns timestamp of latest processed commit/abort record. - * - * When the server has been started normally without recovery the function - * returns NULL. - */ -Datum -pg_last_xact_replay_timestamp(PG_FUNCTION_ARGS) -{ - TimestampTz xtime; - - xtime = GetLatestXTime(); - if (xtime == 0) - PG_RETURN_NULL(); - - PG_RETURN_TIMESTAMPTZ(xtime); -} - -/* - * Returns bool with current recovery mode, a global state. - */ -Datum -pg_is_in_recovery(PG_FUNCTION_ARGS) -{ - PG_RETURN_BOOL(RecoveryInProgress()); -} - /* * Returns time of receipt of current chunk of XLOG data, as well as * whether it was received from streaming replication or from archives. @@ -8835,34 +8739,6 @@ issue_xlog_fsync(int fd, uint32 log, uint32 seg) } } - -/* - * pg_start_backup: set up for taking an on-line backup dump - * - * Essentially what this does is to create a backup label file in $PGDATA, - * where it will be archived as part of the backup dump. The label file - * contains the user-supplied label string (typically this would be used - * to tell where the backup dump will be stored) and the starting time and - * starting WAL location for the dump. - */ -Datum -pg_start_backup(PG_FUNCTION_ARGS) -{ - text *backupid = PG_GETARG_TEXT_P(0); - bool fast = PG_GETARG_BOOL(1); - char *backupidstr; - XLogRecPtr startpoint; - char startxlogstr[MAXFNAMELEN]; - - backupidstr = text_to_cstring(backupid); - - startpoint = do_pg_start_backup(backupidstr, fast, NULL); - - snprintf(startxlogstr, sizeof(startxlogstr), "%X/%X", - startpoint.xlogid, startpoint.xrecoff); - PG_RETURN_TEXT_P(cstring_to_text(startxlogstr)); -} - /* * do_pg_start_backup is the workhorse of the user-visible pg_start_backup() * function. It creates the necessary starting checkpoint and constructs the @@ -9122,32 +8998,6 @@ pg_start_backup_callback(int code, Datum arg) LWLockRelease(WALInsertLock); } -/* - * pg_stop_backup: finish taking an on-line backup dump - * - * We write an end-of-backup WAL record, and remove the backup label file - * created by pg_start_backup, creating a backup history file in pg_xlog - * instead (whence it will immediately be archived). The backup history file - * contains the same info found in the label file, plus the backup-end time - * and WAL location. Before 9.0, the backup-end time was read from the backup - * history file at the beginning of archive recovery, but we now use the WAL - * record for that and the file is for informational and debug purposes only. - * - * Note: different from CancelBackup which just cancels online backup mode. - */ -Datum -pg_stop_backup(PG_FUNCTION_ARGS) -{ - XLogRecPtr stoppoint; - char stopxlogstr[MAXFNAMELEN]; - - stoppoint = do_pg_stop_backup(NULL, true); - - snprintf(stopxlogstr, sizeof(stopxlogstr), "%X/%X", - stoppoint.xlogid, stoppoint.xrecoff); - PG_RETURN_TEXT_P(cstring_to_text(stopxlogstr)); -} - /* * do_pg_stop_backup is the workhorse of the user-visible pg_stop_backup() * function. @@ -9433,166 +9283,6 @@ do_pg_abort_backup(void) LWLockRelease(WALInsertLock); } -/* - * pg_switch_xlog: switch to next xlog file - */ -Datum -pg_switch_xlog(PG_FUNCTION_ARGS) -{ - XLogRecPtr switchpoint; - char location[MAXFNAMELEN]; - - if (!superuser()) - ereport(ERROR, - (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - (errmsg("must be superuser to switch transaction log files")))); - - if (RecoveryInProgress()) - ereport(ERROR, - (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), - errmsg("recovery is in progress"), - errhint("WAL control functions cannot be executed during recovery."))); - - switchpoint = RequestXLogSwitch(); - - /* - * As a convenience, return the WAL location of the switch record - */ - snprintf(location, sizeof(location), "%X/%X", - switchpoint.xlogid, switchpoint.xrecoff); - PG_RETURN_TEXT_P(cstring_to_text(location)); -} - -/* - * pg_create_restore_point: a named point for restore - */ -Datum -pg_create_restore_point(PG_FUNCTION_ARGS) -{ - text *restore_name = PG_GETARG_TEXT_P(0); - char *restore_name_str; - XLogRecPtr restorepoint; - char location[MAXFNAMELEN]; - - if (!superuser()) - ereport(ERROR, - (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - (errmsg("must be superuser to create a restore point")))); - - if (RecoveryInProgress()) - ereport(ERROR, - (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), - (errmsg("recovery is in progress"), - errhint("WAL control functions cannot be executed during recovery.")))); - - if (!XLogIsNeeded()) - ereport(ERROR, - (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), - errmsg("WAL level not sufficient for creating a restore point"), - errhint("wal_level must be set to \"archive\" or \"hot_standby\" at server start."))); - - restore_name_str = text_to_cstring(restore_name); - - if (strlen(restore_name_str) >= MAXFNAMELEN) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("value too long for restore point (maximum %d characters)", MAXFNAMELEN - 1))); - - restorepoint = XLogRestorePoint(restore_name_str); - - /* - * As a convenience, return the WAL location of the restore point record - */ - snprintf(location, sizeof(location), "%X/%X", - restorepoint.xlogid, restorepoint.xrecoff); - PG_RETURN_TEXT_P(cstring_to_text(location)); -} - -/* - * Report the current WAL write location (same format as pg_start_backup etc) - * - * This is useful for determining how much of WAL is visible to an external - * archiving process. Note that the data before this point is written out - * to the kernel, but is not necessarily synced to disk. - */ -Datum -pg_current_xlog_location(PG_FUNCTION_ARGS) -{ - char location[MAXFNAMELEN]; - - if (RecoveryInProgress()) - ereport(ERROR, - (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), - errmsg("recovery is in progress"), - errhint("WAL control functions cannot be executed during recovery."))); - - /* Make sure we have an up-to-date local LogwrtResult */ - { - /* use volatile pointer to prevent code rearrangement */ - volatile XLogCtlData *xlogctl = XLogCtl; - - SpinLockAcquire(&xlogctl->info_lck); - LogwrtResult = xlogctl->LogwrtResult; - SpinLockRelease(&xlogctl->info_lck); - } - - snprintf(location, sizeof(location), "%X/%X", - LogwrtResult.Write.xlogid, LogwrtResult.Write.xrecoff); - PG_RETURN_TEXT_P(cstring_to_text(location)); -} - -/* - * Report the current WAL insert location (same format as pg_start_backup etc) - * - * This function is mostly for debugging purposes. - */ -Datum -pg_current_xlog_insert_location(PG_FUNCTION_ARGS) -{ - XLogCtlInsert *Insert = &XLogCtl->Insert; - XLogRecPtr current_recptr; - char location[MAXFNAMELEN]; - - if (RecoveryInProgress()) - ereport(ERROR, - (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), - errmsg("recovery is in progress"), - errhint("WAL control functions cannot be executed during recovery."))); - - /* - * Get the current end-of-WAL position ... shared lock is sufficient - */ - LWLockAcquire(WALInsertLock, LW_SHARED); - INSERT_RECPTR(current_recptr, Insert, Insert->curridx); - LWLockRelease(WALInsertLock); - - snprintf(location, sizeof(location), "%X/%X", - current_recptr.xlogid, current_recptr.xrecoff); - PG_RETURN_TEXT_P(cstring_to_text(location)); -} - -/* - * Report the last WAL receive location (same format as pg_start_backup etc) - * - * This is useful for determining how much of WAL is guaranteed to be received - * and synced to disk by walreceiver. - */ -Datum -pg_last_xlog_receive_location(PG_FUNCTION_ARGS) -{ - XLogRecPtr recptr; - char location[MAXFNAMELEN]; - - recptr = GetWalRcvWriteRecPtr(NULL); - - if (recptr.xlogid == 0 && recptr.xrecoff == 0) - PG_RETURN_NULL(); - - snprintf(location, sizeof(location), "%X/%X", - recptr.xlogid, recptr.xrecoff); - PG_RETURN_TEXT_P(cstring_to_text(location)); -} - /* * Get latest redo apply position. * @@ -9639,149 +9329,39 @@ GetStandbyFlushRecPtr(void) } /* - * Report the last WAL replay location (same format as pg_start_backup etc) - * - * This is useful for determining how much of WAL is visible to read-only - * connections during recovery. - */ -Datum -pg_last_xlog_replay_location(PG_FUNCTION_ARGS) -{ - XLogRecPtr recptr; - char location[MAXFNAMELEN]; - - recptr = GetXLogReplayRecPtr(NULL); - - if (recptr.xlogid == 0 && recptr.xrecoff == 0) - PG_RETURN_NULL(); - - snprintf(location, sizeof(location), "%X/%X", - recptr.xlogid, recptr.xrecoff); - PG_RETURN_TEXT_P(cstring_to_text(location)); -} - -/* - * Compute an xlog file name and decimal byte offset given a WAL location, - * such as is returned by pg_stop_backup() or pg_xlog_switch(). - * - * Note that a location exactly at a segment boundary is taken to be in - * the previous segment. This is usually the right thing, since the - * expected usage is to determine which xlog file(s) are ready to archive. + * Get latest WAL insert pointer */ -Datum -pg_xlogfile_name_offset(PG_FUNCTION_ARGS) +XLogRecPtr +GetXLogInsertRecPtr(bool needlock) { - text *location = PG_GETARG_TEXT_P(0); - char *locationstr; - unsigned int uxlogid; - unsigned int uxrecoff; - uint32 xlogid; - uint32 xlogseg; - uint32 xrecoff; - XLogRecPtr locationpoint; - char xlogfilename[MAXFNAMELEN]; - Datum values[2]; - bool isnull[2]; - TupleDesc resultTupleDesc; - HeapTuple resultHeapTuple; - Datum result; - - if (RecoveryInProgress()) - ereport(ERROR, - (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), - errmsg("recovery is in progress"), - errhint("pg_xlogfile_name_offset() cannot be executed during recovery."))); - - /* - * Read input and parse - */ - locationstr = text_to_cstring(location); - - if (sscanf(locationstr, "%X/%X", &uxlogid, &uxrecoff) != 2) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("could not parse transaction log location \"%s\"", - locationstr))); - - locationpoint.xlogid = uxlogid; - locationpoint.xrecoff = uxrecoff; - - /* - * Construct a tuple descriptor for the result row. This must match this - * function's pg_proc entry! - */ - resultTupleDesc = CreateTemplateTupleDesc(2, false); - TupleDescInitEntry(resultTupleDesc, (AttrNumber) 1, "file_name", - TEXTOID, -1, 0); - TupleDescInitEntry(resultTupleDesc, (AttrNumber) 2, "file_offset", - INT4OID, -1, 0); - - resultTupleDesc = BlessTupleDesc(resultTupleDesc); - - /* - * xlogfilename - */ - XLByteToPrevSeg(locationpoint, xlogid, xlogseg); - XLogFileName(xlogfilename, ThisTimeLineID, xlogid, xlogseg); - - values[0] = CStringGetTextDatum(xlogfilename); - isnull[0] = false; - - /* - * offset - */ - xrecoff = locationpoint.xrecoff - xlogseg * XLogSegSize; - - values[1] = UInt32GetDatum(xrecoff); - isnull[1] = false; - - /* - * Tuple jam: Having first prepared your Datums, then squash together - */ - resultHeapTuple = heap_form_tuple(resultTupleDesc, values, isnull); + XLogCtlInsert *Insert = &XLogCtl->Insert; + XLogRecPtr current_recptr; - result = HeapTupleGetDatum(resultHeapTuple); + if (needlock) + LWLockAcquire(WALInsertLock, LW_SHARED); + INSERT_RECPTR(current_recptr, Insert, Insert->curridx); + if (needlock) + LWLockRelease(WALInsertLock); - PG_RETURN_DATUM(result); + return current_recptr; } /* - * Compute an xlog file name given a WAL location, - * such as is returned by pg_stop_backup() or pg_xlog_switch(). + * Get latest WAL write pointer */ -Datum -pg_xlogfile_name(PG_FUNCTION_ARGS) +XLogRecPtr +GetXLogWriteRecPtr(void) { - text *location = PG_GETARG_TEXT_P(0); - char *locationstr; - unsigned int uxlogid; - unsigned int uxrecoff; - uint32 xlogid; - uint32 xlogseg; - XLogRecPtr locationpoint; - char xlogfilename[MAXFNAMELEN]; - - if (RecoveryInProgress()) - ereport(ERROR, - (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), - errmsg("recovery is in progress"), - errhint("pg_xlogfile_name() cannot be executed during recovery."))); - - locationstr = text_to_cstring(location); - - if (sscanf(locationstr, "%X/%X", &uxlogid, &uxrecoff) != 2) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("could not parse transaction log location \"%s\"", - locationstr))); - - locationpoint.xlogid = uxlogid; - locationpoint.xrecoff = uxrecoff; + { + /* use volatile pointer to prevent code rearrangement */ + volatile XLogCtlData *xlogctl = XLogCtl; - XLByteToPrevSeg(locationpoint, xlogid, xlogseg); - XLogFileName(xlogfilename, ThisTimeLineID, xlogid, xlogseg); + SpinLockAcquire(&xlogctl->info_lck); + LogwrtResult = xlogctl->LogwrtResult; + SpinLockRelease(&xlogctl->info_lck); + } - PG_RETURN_TEXT_P(cstring_to_text(xlogfilename)); + return LogwrtResult.Write; } /* diff --git a/src/backend/access/transam/xlogfuncs.c b/src/backend/access/transam/xlogfuncs.c new file mode 100644 index 0000000000..22c6ca0426 --- /dev/null +++ b/src/backend/access/transam/xlogfuncs.c @@ -0,0 +1,467 @@ +/*------------------------------------------------------------------------- + * + * xlogfuncs.c + * + * PostgreSQL transaction log manager user interface functions + * + * This file contains WAL control and information functions. + * + * + * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/backend/access/transam/xlogfuncs.c + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "access/xlog.h" +#include "access/xlog_internal.h" +#include "access/xlogutils.h" +#include "catalog/catalog.h" +#include "catalog/pg_type.h" +#include "funcapi.h" +#include "miscadmin.h" +#include "replication/walreceiver.h" +#include "storage/smgr.h" +#include "utils/builtins.h" +#include "utils/guc.h" +#include "utils/timestamp.h" + +/* + * pg_start_backup: set up for taking an on-line backup dump + * + * Essentially what this does is to create a backup label file in $PGDATA, + * where it will be archived as part of the backup dump. The label file + * contains the user-supplied label string (typically this would be used + * to tell where the backup dump will be stored) and the starting time and + * starting WAL location for the dump. + */ +Datum +pg_start_backup(PG_FUNCTION_ARGS) +{ + text *backupid = PG_GETARG_TEXT_P(0); + bool fast = PG_GETARG_BOOL(1); + char *backupidstr; + XLogRecPtr startpoint; + char startxlogstr[MAXFNAMELEN]; + + backupidstr = text_to_cstring(backupid); + + startpoint = do_pg_start_backup(backupidstr, fast, NULL); + + snprintf(startxlogstr, sizeof(startxlogstr), "%X/%X", + startpoint.xlogid, startpoint.xrecoff); + PG_RETURN_TEXT_P(cstring_to_text(startxlogstr)); +} + +/* + * pg_stop_backup: finish taking an on-line backup dump + * + * We write an end-of-backup WAL record, and remove the backup label file + * created by pg_start_backup, creating a backup history file in pg_xlog + * instead (whence it will immediately be archived). The backup history file + * contains the same info found in the label file, plus the backup-end time + * and WAL location. Before 9.0, the backup-end time was read from the backup + * history file at the beginning of archive recovery, but we now use the WAL + * record for that and the file is for informational and debug purposes only. + * + * Note: different from CancelBackup which just cancels online backup mode. + */ +Datum +pg_stop_backup(PG_FUNCTION_ARGS) +{ + XLogRecPtr stoppoint; + char stopxlogstr[MAXFNAMELEN]; + + stoppoint = do_pg_stop_backup(NULL, true); + + snprintf(stopxlogstr, sizeof(stopxlogstr), "%X/%X", + stoppoint.xlogid, stoppoint.xrecoff); + PG_RETURN_TEXT_P(cstring_to_text(stopxlogstr)); +} + +/* + * pg_switch_xlog: switch to next xlog file + */ +Datum +pg_switch_xlog(PG_FUNCTION_ARGS) +{ + XLogRecPtr switchpoint; + char location[MAXFNAMELEN]; + + if (!superuser()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + (errmsg("must be superuser to switch transaction log files")))); + + if (RecoveryInProgress()) + ereport(ERROR, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("recovery is in progress"), + errhint("WAL control functions cannot be executed during recovery."))); + + switchpoint = RequestXLogSwitch(); + + /* + * As a convenience, return the WAL location of the switch record + */ + snprintf(location, sizeof(location), "%X/%X", + switchpoint.xlogid, switchpoint.xrecoff); + PG_RETURN_TEXT_P(cstring_to_text(location)); +} + +/* + * pg_create_restore_point: a named point for restore + */ +Datum +pg_create_restore_point(PG_FUNCTION_ARGS) +{ + text *restore_name = PG_GETARG_TEXT_P(0); + char *restore_name_str; + XLogRecPtr restorepoint; + char location[MAXFNAMELEN]; + + if (!superuser()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + (errmsg("must be superuser to create a restore point")))); + + if (RecoveryInProgress()) + ereport(ERROR, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + (errmsg("recovery is in progress"), + errhint("WAL control functions cannot be executed during recovery.")))); + + if (!XLogIsNeeded()) + ereport(ERROR, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("WAL level not sufficient for creating a restore point"), + errhint("wal_level must be set to \"archive\" or \"hot_standby\" at server start."))); + + restore_name_str = text_to_cstring(restore_name); + + if (strlen(restore_name_str) >= MAXFNAMELEN) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("value too long for restore point (maximum %d characters)", MAXFNAMELEN - 1))); + + restorepoint = XLogRestorePoint(restore_name_str); + + /* + * As a convenience, return the WAL location of the restore point record + */ + snprintf(location, sizeof(location), "%X/%X", + restorepoint.xlogid, restorepoint.xrecoff); + PG_RETURN_TEXT_P(cstring_to_text(location)); +} + +/* + * Report the current WAL write location (same format as pg_start_backup etc) + * + * This is useful for determining how much of WAL is visible to an external + * archiving process. Note that the data before this point is written out + * to the kernel, but is not necessarily synced to disk. + */ +Datum +pg_current_xlog_location(PG_FUNCTION_ARGS) +{ + XLogRecPtr current_recptr; + char location[MAXFNAMELEN]; + + if (RecoveryInProgress()) + ereport(ERROR, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("recovery is in progress"), + errhint("WAL control functions cannot be executed during recovery."))); + + current_recptr = GetXLogWriteRecPtr(); + + snprintf(location, sizeof(location), "%X/%X", + current_recptr.xlogid, current_recptr.xrecoff); + PG_RETURN_TEXT_P(cstring_to_text(location)); +} + +/* + * Report the current WAL insert location (same format as pg_start_backup etc) + * + * This function is mostly for debugging purposes. + */ +Datum +pg_current_xlog_insert_location(PG_FUNCTION_ARGS) +{ + XLogRecPtr current_recptr; + char location[MAXFNAMELEN]; + + if (RecoveryInProgress()) + ereport(ERROR, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("recovery is in progress"), + errhint("WAL control functions cannot be executed during recovery."))); + + current_recptr = GetXLogInsertRecPtr(true); + + snprintf(location, sizeof(location), "%X/%X", + current_recptr.xlogid, current_recptr.xrecoff); + PG_RETURN_TEXT_P(cstring_to_text(location)); +} + +/* + * Report the last WAL receive location (same format as pg_start_backup etc) + * + * This is useful for determining how much of WAL is guaranteed to be received + * and synced to disk by walreceiver. + */ +Datum +pg_last_xlog_receive_location(PG_FUNCTION_ARGS) +{ + XLogRecPtr recptr; + char location[MAXFNAMELEN]; + + recptr = GetWalRcvWriteRecPtr(NULL); + + if (recptr.xlogid == 0 && recptr.xrecoff == 0) + PG_RETURN_NULL(); + + snprintf(location, sizeof(location), "%X/%X", + recptr.xlogid, recptr.xrecoff); + PG_RETURN_TEXT_P(cstring_to_text(location)); +} + +/* + * Report the last WAL replay location (same format as pg_start_backup etc) + * + * This is useful for determining how much of WAL is visible to read-only + * connections during recovery. + */ +Datum +pg_last_xlog_replay_location(PG_FUNCTION_ARGS) +{ + XLogRecPtr recptr; + char location[MAXFNAMELEN]; + + recptr = GetXLogReplayRecPtr(NULL); + + if (recptr.xlogid == 0 && recptr.xrecoff == 0) + PG_RETURN_NULL(); + + snprintf(location, sizeof(location), "%X/%X", + recptr.xlogid, recptr.xrecoff); + PG_RETURN_TEXT_P(cstring_to_text(location)); +} + +/* + * Compute an xlog file name and decimal byte offset given a WAL location, + * such as is returned by pg_stop_backup() or pg_xlog_switch(). + * + * Note that a location exactly at a segment boundary is taken to be in + * the previous segment. This is usually the right thing, since the + * expected usage is to determine which xlog file(s) are ready to archive. + */ +Datum +pg_xlogfile_name_offset(PG_FUNCTION_ARGS) +{ + text *location = PG_GETARG_TEXT_P(0); + char *locationstr; + unsigned int uxlogid; + unsigned int uxrecoff; + uint32 xlogid; + uint32 xlogseg; + uint32 xrecoff; + XLogRecPtr locationpoint; + char xlogfilename[MAXFNAMELEN]; + Datum values[2]; + bool isnull[2]; + TupleDesc resultTupleDesc; + HeapTuple resultHeapTuple; + Datum result; + + if (RecoveryInProgress()) + ereport(ERROR, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("recovery is in progress"), + errhint("pg_xlogfile_name_offset() cannot be executed during recovery."))); + + /* + * Read input and parse + */ + locationstr = text_to_cstring(location); + + if (sscanf(locationstr, "%X/%X", &uxlogid, &uxrecoff) != 2) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("could not parse transaction log location \"%s\"", + locationstr))); + + locationpoint.xlogid = uxlogid; + locationpoint.xrecoff = uxrecoff; + + /* + * Construct a tuple descriptor for the result row. This must match this + * function's pg_proc entry! + */ + resultTupleDesc = CreateTemplateTupleDesc(2, false); + TupleDescInitEntry(resultTupleDesc, (AttrNumber) 1, "file_name", + TEXTOID, -1, 0); + TupleDescInitEntry(resultTupleDesc, (AttrNumber) 2, "file_offset", + INT4OID, -1, 0); + + resultTupleDesc = BlessTupleDesc(resultTupleDesc); + + /* + * xlogfilename + */ + XLByteToPrevSeg(locationpoint, xlogid, xlogseg); + XLogFileName(xlogfilename, ThisTimeLineID, xlogid, xlogseg); + + values[0] = CStringGetTextDatum(xlogfilename); + isnull[0] = false; + + /* + * offset + */ + xrecoff = locationpoint.xrecoff - xlogseg * XLogSegSize; + + values[1] = UInt32GetDatum(xrecoff); + isnull[1] = false; + + /* + * Tuple jam: Having first prepared your Datums, then squash together + */ + resultHeapTuple = heap_form_tuple(resultTupleDesc, values, isnull); + + result = HeapTupleGetDatum(resultHeapTuple); + + PG_RETURN_DATUM(result); +} + +/* + * Compute an xlog file name given a WAL location, + * such as is returned by pg_stop_backup() or pg_xlog_switch(). + */ +Datum +pg_xlogfile_name(PG_FUNCTION_ARGS) +{ + text *location = PG_GETARG_TEXT_P(0); + char *locationstr; + unsigned int uxlogid; + unsigned int uxrecoff; + uint32 xlogid; + uint32 xlogseg; + XLogRecPtr locationpoint; + char xlogfilename[MAXFNAMELEN]; + + if (RecoveryInProgress()) + ereport(ERROR, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("recovery is in progress"), + errhint("pg_xlogfile_name() cannot be executed during recovery."))); + + locationstr = text_to_cstring(location); + + if (sscanf(locationstr, "%X/%X", &uxlogid, &uxrecoff) != 2) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("could not parse transaction log location \"%s\"", + locationstr))); + + locationpoint.xlogid = uxlogid; + locationpoint.xrecoff = uxrecoff; + + XLByteToPrevSeg(locationpoint, xlogid, xlogseg); + XLogFileName(xlogfilename, ThisTimeLineID, xlogid, xlogseg); + + PG_RETURN_TEXT_P(cstring_to_text(xlogfilename)); +} + +/* + * pg_xlog_replay_pause - pause recovery now + */ +Datum +pg_xlog_replay_pause(PG_FUNCTION_ARGS) +{ + if (!superuser()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + (errmsg("must be superuser to control recovery")))); + + if (!RecoveryInProgress()) + ereport(ERROR, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("recovery is not in progress"), + errhint("Recovery control functions can only be executed during recovery."))); + + SetRecoveryPause(true); + + PG_RETURN_VOID(); +} + +/* + * pg_xlog_replay_resume - resume recovery now + */ +Datum +pg_xlog_replay_resume(PG_FUNCTION_ARGS) +{ + if (!superuser()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + (errmsg("must be superuser to control recovery")))); + + if (!RecoveryInProgress()) + ereport(ERROR, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("recovery is not in progress"), + errhint("Recovery control functions can only be executed during recovery."))); + + SetRecoveryPause(false); + + PG_RETURN_VOID(); +} + +/* + * pg_is_xlog_replay_paused + */ +Datum +pg_is_xlog_replay_paused(PG_FUNCTION_ARGS) +{ + if (!superuser()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + (errmsg("must be superuser to control recovery")))); + + if (!RecoveryInProgress()) + ereport(ERROR, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("recovery is not in progress"), + errhint("Recovery control functions can only be executed during recovery."))); + + PG_RETURN_BOOL(RecoveryIsPaused()); +} + +/* + * Returns timestamp of latest processed commit/abort record. + * + * When the server has been started normally without recovery the function + * returns NULL. + */ +Datum +pg_last_xact_replay_timestamp(PG_FUNCTION_ARGS) +{ + TimestampTz xtime; + + xtime = GetLatestXTime(); + if (xtime == 0) + PG_RETURN_NULL(); + + PG_RETURN_TIMESTAMPTZ(xtime); +} + +/* + * Returns bool with current recovery mode, a global state. + */ +Datum +pg_is_in_recovery(PG_FUNCTION_ARGS) +{ + PG_RETURN_BOOL(RecoveryInProgress()); +} diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h index 6c7b021673..83106b80e4 100644 --- a/src/include/access/xlog.h +++ b/src/include/access/xlog.h @@ -293,6 +293,11 @@ extern bool XLogInsertAllowed(void); extern void GetXLogReceiptTime(TimestampTz *rtime, bool *fromStream); extern XLogRecPtr GetXLogReplayRecPtr(XLogRecPtr *restoreLastRecPtr); extern XLogRecPtr GetStandbyFlushRecPtr(void); +extern XLogRecPtr GetXLogInsertRecPtr(bool needlock); +extern XLogRecPtr GetXLogWriteRecPtr(void); +extern bool RecoveryIsPaused(void); +extern void SetRecoveryPause(bool recoveryPause); +extern TimestampTz GetLatestXTime(void); extern void UpdateControlFile(void); extern uint64 GetSystemIdentifier(void); -- 2.40.0