diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/backend/access/transam/xlog.c | 55 | ||||
-rw-r--r-- | src/backend/access/transam/xlogfuncs.c | 185 | ||||
-rw-r--r-- | src/backend/catalog/system_views.sql | 2 | ||||
-rw-r--r-- | src/backend/replication/basebackup.c | 17 | ||||
-rw-r--r-- | src/include/access/xlog.h | 4 | ||||
-rw-r--r-- | src/include/access/xlog_fn.h | 1 | ||||
-rw-r--r-- | src/include/catalog/catversion.h | 2 | ||||
-rw-r--r-- | src/include/catalog/pg_proc.h | 4 |
8 files changed, 232 insertions, 38 deletions
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index 00140ba8f40..f9644db0c35 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -9768,8 +9768,8 @@ XLogFileNameP(TimeLineID tli, XLogSegNo segno) */ XLogRecPtr do_pg_start_backup(const char *backupidstr, bool fast, TimeLineID *starttli_p, - char **labelfile, DIR *tblspcdir, List **tablespaces, - char **tblspcmapfile, bool infotbssize, + StringInfo labelfile, DIR *tblspcdir, List **tablespaces, + StringInfo tblspcmapfile, bool infotbssize, bool needtblspcmapfile) { bool exclusive = (labelfile == NULL); @@ -9783,8 +9783,6 @@ do_pg_start_backup(const char *backupidstr, bool fast, TimeLineID *starttli_p, XLogSegNo _logSegNo; struct stat stat_buf; FILE *fp; - StringInfoData labelfbuf; - StringInfoData tblspc_mapfbuf; backup_started_in_recovery = RecoveryInProgress(); @@ -9981,7 +9979,8 @@ do_pg_start_backup(const char *backupidstr, bool fast, TimeLineID *starttli_p, /* * Construct tablespace_map file */ - initStringInfo(&tblspc_mapfbuf); + if (exclusive) + tblspcmapfile = makeStringInfo(); datadirpathlen = strlen(DataDir); @@ -10054,7 +10053,7 @@ do_pg_start_backup(const char *backupidstr, bool fast, TimeLineID *starttli_p, if (tablespaces) *tablespaces = lappend(*tablespaces, ti); - appendStringInfo(&tblspc_mapfbuf, "%s %s\n", ti->oid, ti->path); + appendStringInfo(tblspcmapfile, "%s %s\n", ti->oid, ti->path); pfree(buflinkpath.data); #else @@ -10073,23 +10072,24 @@ do_pg_start_backup(const char *backupidstr, bool fast, TimeLineID *starttli_p, /* * Construct backup label file */ - initStringInfo(&labelfbuf); + if (exclusive) + labelfile = makeStringInfo(); /* Use the log timezone here, not the session timezone */ stamp_time = (pg_time_t) time(NULL); pg_strftime(strfbuf, sizeof(strfbuf), "%Y-%m-%d %H:%M:%S %Z", pg_localtime(&stamp_time, log_timezone)); - appendStringInfo(&labelfbuf, "START WAL LOCATION: %X/%X (file %s)\n", + appendStringInfo(labelfile, "START WAL LOCATION: %X/%X (file %s)\n", (uint32) (startpoint >> 32), (uint32) startpoint, xlogfilename); - appendStringInfo(&labelfbuf, "CHECKPOINT LOCATION: %X/%X\n", + appendStringInfo(labelfile, "CHECKPOINT LOCATION: %X/%X\n", (uint32) (checkpointloc >> 32), (uint32) checkpointloc); - appendStringInfo(&labelfbuf, "BACKUP METHOD: %s\n", + appendStringInfo(labelfile, "BACKUP METHOD: %s\n", exclusive ? "pg_start_backup" : "streamed"); - appendStringInfo(&labelfbuf, "BACKUP FROM: %s\n", + appendStringInfo(labelfile, "BACKUP FROM: %s\n", backup_started_in_recovery ? "standby" : "master"); - appendStringInfo(&labelfbuf, "START TIME: %s\n", strfbuf); - appendStringInfo(&labelfbuf, "LABEL: %s\n", backupidstr); + appendStringInfo(labelfile, "START TIME: %s\n", strfbuf); + appendStringInfo(labelfile, "LABEL: %s\n", backupidstr); /* * Okay, write the file, or return its contents to caller. @@ -10123,7 +10123,7 @@ do_pg_start_backup(const char *backupidstr, bool fast, TimeLineID *starttli_p, (errcode_for_file_access(), errmsg("could not create file \"%s\": %m", BACKUP_LABEL_FILE))); - if (fwrite(labelfbuf.data, labelfbuf.len, 1, fp) != 1 || + if (fwrite(labelfile->data, labelfile->len, 1, fp) != 1 || fflush(fp) != 0 || pg_fsync(fileno(fp)) != 0 || ferror(fp) || @@ -10132,10 +10132,12 @@ do_pg_start_backup(const char *backupidstr, bool fast, TimeLineID *starttli_p, (errcode_for_file_access(), errmsg("could not write file \"%s\": %m", BACKUP_LABEL_FILE))); - pfree(labelfbuf.data); + /* Allocated locally for exclusive backups, so free separately */ + pfree(labelfile->data); + pfree(labelfile); /* Write backup tablespace_map file. */ - if (tblspc_mapfbuf.len > 0) + if (tblspcmapfile->len > 0) { if (stat(TABLESPACE_MAP, &stat_buf) != 0) { @@ -10159,7 +10161,7 @@ do_pg_start_backup(const char *backupidstr, bool fast, TimeLineID *starttli_p, (errcode_for_file_access(), errmsg("could not create file \"%s\": %m", TABLESPACE_MAP))); - if (fwrite(tblspc_mapfbuf.data, tblspc_mapfbuf.len, 1, fp) != 1 || + if (fwrite(tblspcmapfile->data, tblspcmapfile->len, 1, fp) != 1 || fflush(fp) != 0 || pg_fsync(fileno(fp)) != 0 || ferror(fp) || @@ -10170,13 +10172,9 @@ do_pg_start_backup(const char *backupidstr, bool fast, TimeLineID *starttli_p, TABLESPACE_MAP))); } - pfree(tblspc_mapfbuf.data); - } - else - { - *labelfile = labelfbuf.data; - if (tblspc_mapfbuf.len > 0) - *tblspcmapfile = tblspc_mapfbuf.data; + /* Allocated locally for exclusive backups, so free separately */ + pfree(tblspcmapfile->data); + pfree(tblspcmapfile); } } PG_END_ENSURE_ERROR_CLEANUP(pg_start_backup_callback, (Datum) BoolGetDatum(exclusive)); @@ -10283,7 +10281,16 @@ do_pg_stop_backup(char *labelfile, bool waitforarchive, TimeLineID *stoptli_p) */ WALInsertLockAcquireExclusive(); if (exclusive) + { + if (!XLogCtl->Insert.exclusiveBackup) + { + WALInsertLockRelease(); + ereport(ERROR, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("exclusive backup not in progress"))); + } XLogCtl->Insert.exclusiveBackup = false; + } else { /* diff --git a/src/backend/access/transam/xlogfuncs.c b/src/backend/access/transam/xlogfuncs.c index 9ec6b2ae4f1..6a2fffc64b1 100644 --- a/src/backend/access/transam/xlogfuncs.c +++ b/src/backend/access/transam/xlogfuncs.c @@ -28,14 +28,37 @@ #include "replication/walreceiver.h" #include "storage/smgr.h" #include "utils/builtins.h" +#include "utils/memutils.h" #include "utils/numeric.h" #include "utils/guc.h" #include "utils/pg_lsn.h" #include "utils/timestamp.h" +#include "utils/tuplestore.h" #include "storage/fd.h" +#include "storage/ipc.h" /* + * Store label file and tablespace map during non-exclusive backups. + */ +static StringInfo label_file; +static StringInfo tblspc_map_file; +static bool exclusive_backup_running = false; +static bool nonexclusive_backup_running = false; + +/* + * Called when the backend exits with a running non-exclusive base backup, + * to clean up state. + */ +static void +nonexclusive_base_backup_cleanup(int code, Datum arg) +{ + do_pg_abort_backup(); + ereport(WARNING, + (errmsg("aborting backup due to backend exiting before pg_stop_backup was called"))); +} + +/* * 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, @@ -49,6 +72,7 @@ pg_start_backup(PG_FUNCTION_ARGS) { text *backupid = PG_GETARG_TEXT_P(0); bool fast = PG_GETARG_BOOL(1); + bool exclusive = PG_GETARG_BOOL(2); char *backupidstr; XLogRecPtr startpoint; DIR *dir; @@ -60,14 +84,42 @@ pg_start_backup(PG_FUNCTION_ARGS) (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("must be superuser or replication role to run a backup"))); + if (exclusive_backup_running || nonexclusive_backup_running) + ereport(ERROR, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("a backup is already in progress in this session"))); + /* Make sure we can open the directory with tablespaces in it */ dir = AllocateDir("pg_tblspc"); if (!dir) ereport(ERROR, (errmsg("could not open directory \"%s\": %m", "pg_tblspc"))); - startpoint = do_pg_start_backup(backupidstr, fast, NULL, NULL, - dir, NULL, NULL, false, true); + if (exclusive) + { + startpoint = do_pg_start_backup(backupidstr, fast, NULL, NULL, + dir, NULL, NULL, false, true); + exclusive_backup_running = true; + } + else + { + MemoryContext oldcontext; + + /* + * Label file and tablespace map file need to be long-lived, since they + * are read in pg_stop_backup. + */ + oldcontext = MemoryContextSwitchTo(TopMemoryContext); + label_file = makeStringInfo(); + tblspc_map_file = makeStringInfo(); + MemoryContextSwitchTo(oldcontext); + + startpoint = do_pg_start_backup(backupidstr, fast, NULL, label_file, + dir, NULL, tblspc_map_file, false, true); + nonexclusive_backup_running = true; + + before_shmem_exit(nonexclusive_base_backup_cleanup, (Datum) 0); + } FreeDir(dir); @@ -86,6 +138,10 @@ pg_start_backup(PG_FUNCTION_ARGS) * record for that and the file is for informational and debug purposes only. * * Note: different from CancelBackup which just cancels online backup mode. + * + * Note: this version is only called to stop an exclusive backup. The function + * pg_stop_backup_v2 (overloaded as pg_stop_backup in SQL) is called to + * stop non-exclusive backups. */ Datum pg_stop_backup(PG_FUNCTION_ARGS) @@ -97,11 +153,136 @@ pg_stop_backup(PG_FUNCTION_ARGS) (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errmsg("must be superuser or replication role to run a backup")))); + if (nonexclusive_backup_running) + ereport(ERROR, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("non-exclusive backup in progress"), + errhint("did you mean to use pg_stop_backup('f')?"))); + + /* + * Exclusive backups were typically started in a different connection, + * so don't try to verify that exclusive_backup_running is set in this one. + * Actual verification that an exclusive backup is in fact running is handled + * inside do_pg_stop_backup. + */ stoppoint = do_pg_stop_backup(NULL, true, NULL); + exclusive_backup_running = false; + PG_RETURN_LSN(stoppoint); } + +/* + * pg_stop_backup_v2: finish taking exclusive or nonexclusive on-line backup. + * + * Works the same as pg_stop_backup, except for non-exclusive backups it returns + * the backup label and tablespace map files as text fields in as part of the + * resultset. + */ +Datum +pg_stop_backup_v2(PG_FUNCTION_ARGS) +{ + ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo; + TupleDesc tupdesc; + Tuplestorestate *tupstore; + MemoryContext per_query_ctx; + MemoryContext oldcontext; + Datum values[3]; + bool nulls[3]; + + bool exclusive = PG_GETARG_BOOL(0); + XLogRecPtr stoppoint; + + /* check to see if caller supports us returning a tuplestore */ + if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo)) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("set-valued function called in context that cannot accept a set"))); + if (!(rsinfo->allowedModes & SFRM_Materialize)) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("materialize mode required, but it is not " \ + "allowed in this context"))); + + if (!superuser() && !has_rolreplication(GetUserId())) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + (errmsg("must be superuser or replication role to run a backup")))); + + /* Build a tuple descriptor for our result type */ + if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) + elog(ERROR, "return type must be a row type"); + + per_query_ctx = rsinfo->econtext->ecxt_per_query_memory; + oldcontext = MemoryContextSwitchTo(per_query_ctx); + + tupstore = tuplestore_begin_heap(true, false, work_mem); + rsinfo->returnMode = SFRM_Materialize; + rsinfo->setResult = tupstore; + rsinfo->setDesc = tupdesc; + + MemoryContextSwitchTo(oldcontext); + + MemSet(values, 0, sizeof(values)); + MemSet(nulls, 0, sizeof(nulls)); + + if (exclusive) + { + if (nonexclusive_backup_running) + ereport(ERROR, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("non-exclusive backup in progress"), + errhint("did you mean to use pg_stop_backup('f')?"))); + + /* + * Stop the exclusive backup, and since we're in an exclusive backup + * return NULL for both backup_label and tablespace_map. + */ + stoppoint = do_pg_stop_backup(NULL, true, NULL); + exclusive_backup_running = false; + + nulls[1] = true; + nulls[2] = true; + } + else + { + if (!nonexclusive_backup_running) + ereport(ERROR, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("non-exclusive backup is not in progress"), + errhint("did you mean to use pg_stop_backup('t')?"))); + + /* + * Stop the non-exclusive backup. Return a copy of the backup + * label and tablespace map so they can be written to disk by + * the caller. + */ + stoppoint = do_pg_stop_backup(label_file->data, true, NULL); + nonexclusive_backup_running = false; + cancel_before_shmem_exit(nonexclusive_base_backup_cleanup, (Datum) 0); + + values[1] = CStringGetTextDatum(label_file->data); + values[2] = CStringGetTextDatum(tblspc_map_file->data); + + /* Free structures allocated in TopMemoryContext */ + pfree(label_file->data); + pfree(label_file); + label_file = NULL; + pfree(tblspc_map_file->data); + pfree(tblspc_map_file); + tblspc_map_file = NULL; + } + + /* Stoppoint is included on both exclusive and nonexclusive backups */ + values[0] = LSNGetDatum(stoppoint); + + tuplestore_putvalues(tupstore, tupdesc, values, nulls); + tuplestore_donestoring(typstore); + + return (Datum) 0; +} + /* * pg_switch_xlog: switch to next xlog file */ diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql index 9ae1ef4efa6..2bd40a160e0 100644 --- a/src/backend/catalog/system_views.sql +++ b/src/backend/catalog/system_views.sql @@ -921,7 +921,7 @@ COMMENT ON FUNCTION ts_debug(text) IS -- CREATE OR REPLACE FUNCTION - pg_start_backup(label text, fast boolean DEFAULT false) + pg_start_backup(label text, fast boolean DEFAULT false, exclusive boolean DEFAULT true) RETURNS pg_lsn STRICT VOLATILE LANGUAGE internal AS 'pg_start_backup'; -- legacy definition for compatibility with 9.3 diff --git a/src/backend/replication/basebackup.c b/src/backend/replication/basebackup.c index af0fb09870d..100887337c0 100644 --- a/src/backend/replication/basebackup.c +++ b/src/backend/replication/basebackup.c @@ -117,8 +117,8 @@ perform_base_backup(basebackup_options *opt, DIR *tblspcdir) TimeLineID starttli; XLogRecPtr endptr; TimeLineID endtli; - char *labelfile; - char *tblspc_map_file = NULL; + StringInfo labelfile; + StringInfo tblspc_map_file = NULL; int datadirpathlen; List *tablespaces = NIL; @@ -126,9 +126,12 @@ perform_base_backup(basebackup_options *opt, DIR *tblspcdir) backup_started_in_recovery = RecoveryInProgress(); + labelfile = makeStringInfo(); + tblspc_map_file = makeStringInfo(); + startptr = do_pg_start_backup(opt->label, opt->fastcheckpoint, &starttli, - &labelfile, tblspcdir, &tablespaces, - &tblspc_map_file, + labelfile, tblspcdir, &tablespaces, + tblspc_map_file, opt->progress, opt->sendtblspcmapfile); /* @@ -206,7 +209,7 @@ perform_base_backup(basebackup_options *opt, DIR *tblspcdir) struct stat statbuf; /* In the main tar, include the backup_label first... */ - sendFileWithContent(BACKUP_LABEL_FILE, labelfile); + sendFileWithContent(BACKUP_LABEL_FILE, labelfile->data); /* * Send tablespace_map file if required and then the bulk of @@ -214,7 +217,7 @@ perform_base_backup(basebackup_options *opt, DIR *tblspcdir) */ if (tblspc_map_file && opt->sendtblspcmapfile) { - sendFileWithContent(TABLESPACE_MAP, tblspc_map_file); + sendFileWithContent(TABLESPACE_MAP, tblspc_map_file->data); sendDir(".", 1, false, tablespaces, false); } else @@ -247,7 +250,7 @@ perform_base_backup(basebackup_options *opt, DIR *tblspcdir) } PG_END_ENSURE_ERROR_CLEANUP(base_backup_cleanup, (Datum) 0); - endptr = do_pg_stop_backup(labelfile, !opt->nowait, &endtli); + endptr = do_pg_stop_backup(labelfile->data, !opt->nowait, &endtli); if (opt->includewal) { diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h index a7dcdae67f8..0744c3f9720 100644 --- a/src/include/access/xlog.h +++ b/src/include/access/xlog.h @@ -276,8 +276,8 @@ extern void assign_checkpoint_completion_target(double newval, void *extra); * Starting/stopping a base backup */ extern XLogRecPtr do_pg_start_backup(const char *backupidstr, bool fast, - TimeLineID *starttli_p, char **labelfile, DIR *tblspcdir, - List **tablespaces, char **tblspcmapfile, bool infotbssize, + TimeLineID *starttli_p, StringInfo labelfile, DIR *tblspcdir, + List **tablespaces, StringInfo tblspcmapfile, bool infotbssize, bool needtblspcmapfile); extern XLogRecPtr do_pg_stop_backup(char *labelfile, bool waitforarchive, TimeLineID *stoptli_p); diff --git a/src/include/access/xlog_fn.h b/src/include/access/xlog_fn.h index 762df8bea30..576a4dc741a 100644 --- a/src/include/access/xlog_fn.h +++ b/src/include/access/xlog_fn.h @@ -15,6 +15,7 @@ extern Datum pg_start_backup(PG_FUNCTION_ARGS); extern Datum pg_stop_backup(PG_FUNCTION_ARGS); +extern Datum pg_stop_backup_v2(PG_FUNCTION_ARGS); extern Datum pg_switch_xlog(PG_FUNCTION_ARGS); extern Datum pg_create_restore_point(PG_FUNCTION_ARGS); extern Datum pg_current_xlog_location(PG_FUNCTION_ARGS); diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 74e4cc4ede4..7e75f8ebb53 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 201603301 +#define CATALOG_VERSION_NO 201604051 #endif diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index c86b920cb65..c0d88ab2933 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -3075,10 +3075,12 @@ DATA(insert OID = 2171 ( pg_cancel_backend PGNSP PGUID 12 1 0 0 0 f f f f t f v DESCR("cancel a server process' current query"); DATA(insert OID = 2096 ( pg_terminate_backend PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 16 "23" _null_ _null_ _null_ _null_ _null_ pg_terminate_backend _null_ _null_ _null_ )); DESCR("terminate a server process"); -DATA(insert OID = 2172 ( pg_start_backup PGNSP PGUID 12 1 0 0 0 f f f f t f v s 2 0 3220 "25 16" _null_ _null_ _null_ _null_ _null_ pg_start_backup _null_ _null_ _null_ )); +DATA(insert OID = 2172 ( pg_start_backup PGNSP PGUID 12 1 0 0 0 f f f f t f v s 3 0 3220 "25 16 16" _null_ _null_ _null_ _null_ _null_ pg_start_backup _null_ _null_ _null_ )); DESCR("prepare for taking an online backup"); DATA(insert OID = 2173 ( pg_stop_backup PGNSP PGUID 12 1 0 0 0 f f f f t f v s 0 0 3220 "" _null_ _null_ _null_ _null_ _null_ pg_stop_backup _null_ _null_ _null_ )); DESCR("finish taking an online backup"); +DATA(insert OID = 2739 ( pg_stop_backup PGNSP PGUID 12 1 1 0 0 f f f f t t v s 1 0 2249 "16" "{16,3220,25,25}" "{i,o,o,o}" "{exclusive,lsn,labelfile,spcmapfile}" _null_ _null_ pg_stop_backup_v2 _null_ _null_ _null_ )); +DESCR("finish taking an online backup"); DATA(insert OID = 3813 ( pg_is_in_backup PGNSP PGUID 12 1 0 0 0 f f f f t f v s 0 0 16 "" _null_ _null_ _null_ _null_ _null_ pg_is_in_backup _null_ _null_ _null_ )); DESCR("true if server is in online backup"); DATA(insert OID = 3814 ( pg_backup_start_time PGNSP PGUID 12 1 0 0 0 f f f f t f s s 0 0 1184 "" _null_ _null_ _null_ _null_ _null_ pg_backup_start_time _null_ _null_ _null_ )); |