diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/backend/access/transam/xlog.c | 232 | ||||
-rw-r--r-- | src/backend/postmaster/pgarch.c | 6 | ||||
-rw-r--r-- | src/backend/utils/adt/misc.c | 82 | ||||
-rw-r--r-- | src/include/access/xlog_internal.h | 17 | ||||
-rw-r--r-- | src/include/catalog/catversion.h | 4 | ||||
-rw-r--r-- | src/include/catalog/pg_proc.h | 17 | ||||
-rw-r--r-- | src/include/utils/builtins.h | 3 |
7 files changed, 313 insertions, 48 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); +} diff --git a/src/backend/postmaster/pgarch.c b/src/backend/postmaster/pgarch.c index a4038963331..960ece75831 100644 --- a/src/backend/postmaster/pgarch.c +++ b/src/backend/postmaster/pgarch.c @@ -19,7 +19,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/postmaster/pgarch.c,v 1.3 2004/08/01 17:45:43 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/postmaster/pgarch.c,v 1.4 2004/08/03 20:32:33 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -64,8 +64,8 @@ * ---------- */ #define MIN_XFN_CHARS 16 -#define MAX_XFN_CHARS 24 -#define VALID_XFN_CHARS "0123456789ABCDEF.history" +#define MAX_XFN_CHARS 40 +#define VALID_XFN_CHARS "0123456789ABCDEF.history.backup" #define NUM_ARCHIVE_RETRIES 3 diff --git a/src/backend/utils/adt/misc.c b/src/backend/utils/adt/misc.c index 0cc315c6205..8bf989a8658 100644 --- a/src/backend/utils/adt/misc.c +++ b/src/backend/utils/adt/misc.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/adt/misc.c,v 1.35 2004/07/02 18:59:22 joe Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/misc.c,v 1.36 2004/08/03 20:32:33 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -27,6 +27,8 @@ #include "catalog/pg_type.h" #include "catalog/pg_tablespace.h" +#define atooid(x) ((Oid) strtoul((x), NULL, 10)) + /* * Check if data is Null @@ -67,8 +69,7 @@ current_database(PG_FUNCTION_ARGS) /* - * Functions to terminate a backend or cancel a query running on - * a different backend. + * Functions to send signals to other backends. */ static int pg_signal_backend(int pid, int sig) @@ -76,14 +77,16 @@ static int pg_signal_backend(int pid, int sig) if (!superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - (errmsg("only superuser can signal other backends")))); + (errmsg("must be superuser to signal other server processes")))); if (!IsBackendPid(pid)) { - /* This is just a warning so a loop-through-resultset will not abort - * if one backend terminated on it's own during the run */ + /* + * This is just a warning so a loop-through-resultset will not abort + * if one backend terminated on it's own during the run + */ ereport(WARNING, - (errmsg("pid %i is not a postgresql backend",pid))); + (errmsg("PID %d is not a PostgreSQL server process", pid))); return 0; } @@ -91,24 +94,32 @@ static int pg_signal_backend(int pid, int sig) { /* Again, just a warning to allow loops */ ereport(WARNING, - (errmsg("failed to send signal to backend %i: %m",pid))); + (errmsg("could not send signal to process %d: %m",pid))); return 0; } return 1; } Datum -pg_terminate_backend(PG_FUNCTION_ARGS) +pg_cancel_backend(PG_FUNCTION_ARGS) { - PG_RETURN_INT32(pg_signal_backend(PG_GETARG_INT32(0),SIGTERM)); + PG_RETURN_INT32(pg_signal_backend(PG_GETARG_INT32(0),SIGINT)); } +#ifdef NOT_USED + +/* Disabled in 8.0 due to reliability concerns; FIXME someday */ + Datum -pg_cancel_backend(PG_FUNCTION_ARGS) +pg_terminate_backend(PG_FUNCTION_ARGS) { - PG_RETURN_INT32(pg_signal_backend(PG_GETARG_INT32(0),SIGINT)); + PG_RETURN_INT32(pg_signal_backend(PG_GETARG_INT32(0),SIGTERM)); } +#endif + + +/* Function to find out which databases make use of a tablespace */ typedef struct { @@ -140,9 +151,8 @@ Datum pg_tablespace_databases(PG_FUNCTION_ARGS) if (tablespaceOid == GLOBALTABLESPACE_OID) { fctx->dirdesc = NULL; - ereport(NOTICE, - (errcode(ERRCODE_WARNING), - errmsg("global tablespace never has databases."))); + ereport(WARNING, + (errmsg("global tablespace never has databases"))); } else { @@ -154,10 +164,17 @@ Datum pg_tablespace_databases(PG_FUNCTION_ARGS) fctx->dirdesc = AllocateDir(fctx->location); - if (!fctx->dirdesc) /* not a tablespace */ - ereport(NOTICE, - (errcode(ERRCODE_WARNING), - errmsg("%d is no tablespace oid.", tablespaceOid))); + if (!fctx->dirdesc) + { + /* the only expected error is ENOENT */ + if (errno != ENOENT) + ereport(ERROR, + (errcode_for_file_access(), + errmsg("could not open directory \"%s\": %m", + fctx->location))); + ereport(WARNING, + (errmsg("%u is not a tablespace oid", tablespaceOid))); + } } funcctx->user_fctx = fctx; MemoryContextSwitchTo(oldcontext); @@ -174,27 +191,30 @@ Datum pg_tablespace_databases(PG_FUNCTION_ARGS) char *subdir; DIR *dirdesc; - Oid datOid = atol(de->d_name); + Oid datOid = atooid(de->d_name); + /* this test skips . and .., but is awfully weak */ if (!datOid) continue; + /* if database subdir is empty, don't report tablespace as used */ + /* size = path length + dir sep char + file name + terminator */ subdir = palloc(strlen(fctx->location) + 1 + strlen(de->d_name) + 1); sprintf(subdir, "%s/%s", fctx->location, de->d_name); dirdesc = AllocateDir(subdir); - if (dirdesc) - { - while ((de = readdir(dirdesc)) != 0) - { - if (strcmp(de->d_name, ".") && strcmp(de->d_name, "..")) - break; - } - pfree(subdir); - FreeDir(dirdesc); + pfree(subdir); + if (!dirdesc) + continue; /* XXX more sloppiness */ - if (!de) /* database subdir is empty; don't report tablespace as used */ - continue; + while ((de = readdir(dirdesc)) != 0) + { + if (strcmp(de->d_name, ".") != 0 && strcmp(de->d_name, "..") != 0) + break; } + FreeDir(dirdesc); + + if (!de) + continue; /* indeed, nothing in it */ SRF_RETURN_NEXT(funcctx, ObjectIdGetDatum(datOid)); } diff --git a/src/include/access/xlog_internal.h b/src/include/access/xlog_internal.h index 09877bf64be..89c37b7d66a 100644 --- a/src/include/access/xlog_internal.h +++ b/src/include/access/xlog_internal.h @@ -11,12 +11,13 @@ * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/access/xlog_internal.h,v 1.1 2004/07/21 22:31:25 tgl Exp $ + * $PostgreSQL: pgsql/src/include/access/xlog_internal.h,v 1.2 2004/08/03 20:32:34 tgl Exp $ */ #ifndef XLOG_INTERNAL_H #define XLOG_INTERNAL_H #include "access/xlog.h" +#include "fmgr.h" #include "storage/block.h" #include "storage/relfilenode.h" @@ -177,7 +178,7 @@ typedef XLogLongPageHeaderData *XLogLongPageHeader; * These macros encapsulate knowledge about the exact layout of XLog file * names, timeline history file names, and archive-status file names. */ -#define MAXFNAMELEN 32 +#define MAXFNAMELEN 64 #define XLogFileName(fname, tli, log, seg) \ snprintf(fname, MAXFNAMELEN, "%08X%08X%08X", tli, log, seg) @@ -194,6 +195,12 @@ typedef XLogLongPageHeaderData *XLogLongPageHeader; #define StatusFilePath(path, xlog, suffix) \ snprintf(path, MAXPGPATH, "%s/archive_status/%s%s", XLogDir, xlog, suffix) +#define BackupHistoryFileName(fname, tli, log, seg, offset) \ + snprintf(fname, MAXFNAMELEN, "%08X%08X%08X.%08X.backup", tli, log, seg, offset) + +#define BackupHistoryFilePath(path, tli, log, seg, offset) \ + snprintf(path, MAXPGPATH, "%s/%08X%08X%08X.%08X.backup", XLogDir, tli, log, seg, offset) + extern char XLogDir[MAXPGPATH]; /* @@ -221,4 +228,10 @@ typedef struct RmgrData extern const RmgrData RmgrTable[]; +/* + * These aren't in xlog.h because I'd rather not include fmgr.h there. + */ +extern Datum pg_start_backup(PG_FUNCTION_ARGS); +extern Datum pg_stop_backup(PG_FUNCTION_ARGS); + #endif /* XLOG_INTERNAL_H */ diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index d7d180eaf51..0d5ac8f13c5 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -37,7 +37,7 @@ * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.247 2004/07/21 20:43:53 momjian Exp $ + * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.248 2004/08/03 20:32:35 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 200407211 +#define CATALOG_VERSION_NO 200408031 #endif diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index 646fa73dd15..21bd60fb946 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -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/include/catalog/pg_proc.h,v 1.342 2004/07/12 20:23:53 momjian Exp $ + * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.343 2004/08/03 20:32:35 tgl Exp $ * * NOTES * The script catalog/genbki.sh reads this file and generates .bki @@ -2815,11 +2815,6 @@ DESCR("Statistics: Blocks fetched for database"); DATA(insert OID = 1945 ( pg_stat_get_db_blocks_hit PGNSP PGUID 12 f f t f s 1 20 "26" _null_ pg_stat_get_db_blocks_hit - _null_ )); DESCR("Statistics: Blocks found in cache for database"); -DATA(insert OID = 2171 ( pg_terminate_backend PGNSP PGUID 12 f f t f s 1 23 "23" _null_ pg_terminate_backend - _null_ )); -DESCR("Terminate a backend process"); -DATA(insert OID = 2172 ( pg_cancel_backend PGNSP PGUID 12 f f t f s 1 23 "23" _null_ pg_cancel_backend - _null_ )); -DESCR("Cancel running query on a backend process"); - DATA(insert OID = 1946 ( encode PGNSP PGUID 12 f f t f i 2 25 "17 25" _null_ binary_encode - _null_ )); DESCR("Convert bytea value into some ascii-only text string"); DATA(insert OID = 1947 ( decode PGNSP PGUID 12 f f t f i 2 17 "25 25" _null_ binary_decode - _null_ )); @@ -2993,10 +2988,18 @@ DATA(insert OID = 2082 ( pg_operator_is_visible PGNSP PGUID 12 f f t f s 1 16 " DESCR("is operator visible in search path?"); DATA(insert OID = 2083 ( pg_opclass_is_visible PGNSP PGUID 12 f f t f s 1 16 "26" _null_ pg_opclass_is_visible - _null_ )); DESCR("is opclass visible in search path?"); -DATA(insert OID = 2093 ( pg_conversion_is_visible PGNSP PGUID 12 f f t f s 1 16 "26" _null_ pg_conversion_is_visible - _null_ )); +DATA(insert OID = 2093 ( pg_conversion_is_visible PGNSP PGUID 12 f f t f s 1 16 "26" _null_ pg_conversion_is_visible - _null_ )); DESCR("is conversion visible in search path?"); +DATA(insert OID = 2171 ( pg_cancel_backend PGNSP PGUID 12 f f t f v 1 23 "23" _null_ pg_cancel_backend - _null_ )); +DESCR("Cancel a server process' current query"); +DATA(insert OID = 2172 ( pg_start_backup PGNSP PGUID 12 f f t f v 1 25 "25" _null_ pg_start_backup - _null_ )); +DESCR("Prepare for taking an online backup"); +DATA(insert OID = 2173 ( pg_stop_backup PGNSP PGUID 12 f f t f v 0 25 "" _null_ pg_stop_backup - _null_ )); +DESCR("Finish taking an online backup"); + + /* Aggregates (moved here from pg_aggregate for 7.3) */ DATA(insert OID = 2100 ( avg PGNSP PGUID 12 t f f f i 1 1700 "20" _null_ aggregate_dummy - _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 3e9f58e7f26..09d91d985c9 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -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/include/utils/builtins.h,v 1.246 2004/07/12 20:23:59 momjian Exp $ + * $PostgreSQL: pgsql/src/include/utils/builtins.h,v 1.247 2004/08/03 20:32:36 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -360,7 +360,6 @@ extern Datum float84ge(PG_FUNCTION_ARGS); extern Datum nullvalue(PG_FUNCTION_ARGS); extern Datum nonnullvalue(PG_FUNCTION_ARGS); extern Datum current_database(PG_FUNCTION_ARGS); -extern Datum pg_terminate_backend(PG_FUNCTION_ARGS); extern Datum pg_cancel_backend(PG_FUNCTION_ARGS); extern Datum pg_tablespace_databases(PG_FUNCTION_ARGS); |