diff options
Diffstat (limited to 'src/backend/access/transam/xlog.c')
-rw-r--r-- | src/backend/access/transam/xlog.c | 232 |
1 files changed, 231 insertions, 1 deletions
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index b0fac745266..59666580035 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/backend/access/transam/xlog.c,v 1.153 2004/08/01 17:45:42 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/access/transam/xlog.c,v 1.154 2004/08/03 20:32:32 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -5048,3 +5048,233 @@ issue_xlog_fsync(void) break; } } + + +/* + * 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 offset for the dump. + */ +Datum +pg_start_backup(PG_FUNCTION_ARGS) +{ + text *backupid = PG_GETARG_TEXT_P(0); + text *result; + char *backupidstr; + XLogRecPtr startpoint; + time_t stamp_time; + char strfbuf[128]; + char labelfilename[MAXPGPATH]; + char xlogfilename[MAXFNAMELEN]; + uint32 _logId; + uint32 _logSeg; + struct stat stat_buf; + FILE *fp; + + if (!superuser()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + (errmsg("must be superuser to run a backup")))); + backupidstr = DatumGetCString(DirectFunctionCall1(textout, + PointerGetDatum(backupid))); + /* + * The oldest point in WAL that would be needed to restore starting from + * the most recent checkpoint is precisely the RedoRecPtr. + */ + startpoint = GetRedoRecPtr(); + XLByteToSeg(startpoint, _logId, _logSeg); + XLogFileName(xlogfilename, ThisTimeLineID, _logId, _logSeg); + /* + * We deliberately use strftime/localtime not the src/timezone functions, + * so that backup labels will consistently be recorded in the same + * timezone regardless of TimeZone setting. This matches elog.c's + * practice. + */ + stamp_time = time(NULL); + strftime(strfbuf, sizeof(strfbuf), + "%Y-%m-%d %H:%M:%S %Z", + localtime(&stamp_time)); + /* + * Check for existing backup label --- implies a backup is already running + */ + snprintf(labelfilename, MAXPGPATH, "%s/backup_label", DataDir); + if (stat(labelfilename, &stat_buf) != 0) + { + if (errno != ENOENT) + ereport(ERROR, + (errcode_for_file_access(), + errmsg("could not stat \"%s\": %m", + labelfilename))); + } + else + ereport(ERROR, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("a backup is already in progress"), + errhint("If you're sure there is no backup in progress, remove file \"%s\" and try again.", + labelfilename))); + /* + * Okay, write the file + */ + fp = AllocateFile(labelfilename, "w"); + if (!fp) + ereport(ERROR, + (errcode_for_file_access(), + errmsg("could not create file \"%s\": %m", + labelfilename))); + fprintf(fp, "START WAL LOCATION: %X/%X (file %s)\n", + startpoint.xlogid, startpoint.xrecoff, xlogfilename); + fprintf(fp, "START TIME: %s\n", strfbuf); + fprintf(fp, "LABEL: %s\n", backupidstr); + if (fflush(fp) || ferror(fp) || FreeFile(fp)) + ereport(ERROR, + (errcode_for_file_access(), + errmsg("could not write file \"%s\": %m", + labelfilename))); + /* + * We're done. As a convenience, return the starting WAL offset. + */ + snprintf(xlogfilename, sizeof(xlogfilename), "%X/%X", + startpoint.xlogid, startpoint.xrecoff); + result = DatumGetTextP(DirectFunctionCall1(textin, + CStringGetDatum(xlogfilename))); + PG_RETURN_TEXT_P(result); +} + +/* + * pg_stop_backup: finish taking an on-line backup dump + * + * We remove the backup label file created by pg_start_backup, and instead + * create a backup history file in pg_xlog (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 offset. + */ +Datum +pg_stop_backup(PG_FUNCTION_ARGS) +{ + text *result; + XLogCtlInsert *Insert = &XLogCtl->Insert; + XLogRecPtr startpoint; + XLogRecPtr stoppoint; + time_t stamp_time; + char strfbuf[128]; + char labelfilename[MAXPGPATH]; + char histfilename[MAXPGPATH]; + char startxlogfilename[MAXFNAMELEN]; + char stopxlogfilename[MAXFNAMELEN]; + uint32 _logId; + uint32 _logSeg; + FILE *lfp; + FILE *fp; + char ch; + int ich; + + if (!superuser()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + (errmsg("must be superuser to run a backup")))); + /* + * Get the current end-of-WAL position; it will be unsafe to use this + * dump to restore to a point in advance of this time. + */ + LWLockAcquire(WALInsertLock, LW_EXCLUSIVE); + INSERT_RECPTR(stoppoint, Insert, Insert->curridx); + LWLockRelease(WALInsertLock); + + XLByteToSeg(stoppoint, _logId, _logSeg); + XLogFileName(stopxlogfilename, ThisTimeLineID, _logId, _logSeg); + /* + * We deliberately use strftime/localtime not the src/timezone functions, + * so that backup labels will consistently be recorded in the same + * timezone regardless of TimeZone setting. This matches elog.c's + * practice. + */ + stamp_time = time(NULL); + strftime(strfbuf, sizeof(strfbuf), + "%Y-%m-%d %H:%M:%S %Z", + localtime(&stamp_time)); + /* + * Open the existing label file + */ + snprintf(labelfilename, MAXPGPATH, "%s/backup_label", DataDir); + lfp = AllocateFile(labelfilename, "r"); + if (!lfp) + { + if (errno != ENOENT) + ereport(ERROR, + (errcode_for_file_access(), + errmsg("could not read file \"%s\": %m", + labelfilename))); + ereport(ERROR, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("a backup is not in progress"))); + } + /* + * Read and parse the START WAL LOCATION line (this code is pretty + * crude, but we are not expecting any variability in the file format). + */ + if (fscanf(lfp, "START WAL LOCATION: %X/%X (file %24s)%c", + &startpoint.xlogid, &startpoint.xrecoff, startxlogfilename, + &ch) != 4 || ch != '\n') + ereport(ERROR, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("invalid data in file \"%s\"", labelfilename))); + /* + * Write the backup history file + */ + XLByteToSeg(startpoint, _logId, _logSeg); + BackupHistoryFilePath(histfilename, ThisTimeLineID, _logId, _logSeg, + startpoint.xrecoff % XLogSegSize); + fp = AllocateFile(histfilename, "w"); + if (!fp) + ereport(ERROR, + (errcode_for_file_access(), + errmsg("could not create file \"%s\": %m", + histfilename))); + fprintf(fp, "START WAL LOCATION: %X/%X (file %s)\n", + startpoint.xlogid, startpoint.xrecoff, startxlogfilename); + fprintf(fp, "STOP WAL LOCATION: %X/%X (file %s)\n", + stoppoint.xlogid, stoppoint.xrecoff, stopxlogfilename); + /* transfer start time and label lines from label to history file */ + while ((ich = fgetc(lfp)) != EOF) + fputc(ich, fp); + fprintf(fp, "STOP TIME: %s\n", strfbuf); + if (fflush(fp) || ferror(fp) || FreeFile(fp)) + ereport(ERROR, + (errcode_for_file_access(), + errmsg("could not write file \"%s\": %m", + histfilename))); + /* + * Close and remove the backup label file + */ + if (ferror(lfp) || FreeFile(lfp)) + ereport(ERROR, + (errcode_for_file_access(), + errmsg("could not read file \"%s\": %m", + labelfilename))); + if (unlink(labelfilename) != 0) + ereport(ERROR, + (errcode_for_file_access(), + errmsg("could not remove file \"%s\": %m", + labelfilename))); + /* + * Notify archiver that history file may be archived immediately + */ + if (XLogArchivingActive()) + { + BackupHistoryFileName(histfilename, ThisTimeLineID, _logId, _logSeg, + startpoint.xrecoff % XLogSegSize); + XLogArchiveNotify(histfilename); + } + /* + * We're done. As a convenience, return the ending WAL offset. + */ + snprintf(stopxlogfilename, sizeof(stopxlogfilename), "%X/%X", + stoppoint.xlogid, stoppoint.xrecoff); + result = DatumGetTextP(DirectFunctionCall1(textin, + CStringGetDatum(stopxlogfilename))); + PG_RETURN_TEXT_P(result); +} |