mirror of
https://github.com/pgbackrest/pgbackrest.git
synced 2024-12-12 10:04:14 +02:00
Add backup functions to Db object.
These functions implement the database backup functionality for all supported versions.
This commit is contained in:
parent
8766326da8
commit
d2587250da
@ -474,7 +474,7 @@ config/parse.o: config/parse.c build.auto.h common/assert.h common/debug.h commo
|
||||
config/protocol.o: config/protocol.c build.auto.h common/assert.h common/debug.h common/error.auto.h common/error.h common/io/filter/filter.h common/io/filter/group.h common/io/io.h common/io/read.h common/io/write.h common/lock.h common/log.h common/logLevel.h common/memContext.h common/stackTrace.h common/time.h common/type/buffer.h common/type/convert.h common/type/keyValue.h common/type/list.h common/type/param.h common/type/string.h common/type/stringList.h common/type/variant.h common/type/variantList.h config/config.auto.h config/config.h config/define.auto.h config/define.h config/protocol.h protocol/client.h protocol/command.h protocol/server.h
|
||||
$(CC) $(CPPFLAGS) $(CFLAGS) $(CMAKE) -c config/protocol.c -o config/protocol.o
|
||||
|
||||
db/db.o: db/db.c build.auto.h common/assert.h common/debug.h common/error.auto.h common/error.h common/io/filter/filter.h common/io/filter/group.h common/io/read.h common/io/write.h common/log.h common/logLevel.h common/macro.h common/memContext.h common/object.h common/stackTrace.h common/time.h common/type/buffer.h common/type/convert.h common/type/keyValue.h common/type/list.h common/type/param.h common/type/string.h common/type/stringList.h common/type/variant.h common/type/variantList.h db/db.h db/protocol.h postgres/client.h postgres/interface.h postgres/version.h protocol/client.h protocol/command.h protocol/server.h storage/info.h storage/read.h storage/storage.h storage/write.h version.h
|
||||
db/db.o: db/db.c build.auto.h common/assert.h common/debug.h common/error.auto.h common/error.h common/io/filter/filter.h common/io/filter/group.h common/io/read.h common/io/write.h common/log.h common/logLevel.h common/macro.h common/memContext.h common/object.h common/stackTrace.h common/time.h common/type/buffer.h common/type/convert.h common/type/keyValue.h common/type/list.h common/type/param.h common/type/string.h common/type/stringList.h common/type/variant.h common/type/variantList.h common/wait.h db/db.h db/protocol.h postgres/client.h postgres/interface.h postgres/version.h protocol/client.h protocol/command.h protocol/helper.h protocol/server.h storage/info.h storage/read.h storage/storage.h storage/write.h version.h
|
||||
$(CC) $(CPPFLAGS) $(CFLAGS) $(CMAKE) -c db/db.c -o db/db.o
|
||||
|
||||
db/helper.o: db/helper.c build.auto.h common/assert.h common/debug.h common/error.auto.h common/error.h common/io/filter/filter.h common/io/filter/group.h common/io/read.h common/io/write.h common/lock.h common/log.h common/logLevel.h common/memContext.h common/stackTrace.h common/time.h common/type/buffer.h common/type/convert.h common/type/keyValue.h common/type/list.h common/type/param.h common/type/string.h common/type/stringList.h common/type/variant.h common/type/variantList.h config/config.auto.h config/config.h config/define.auto.h config/define.h db/db.h db/helper.h postgres/client.h postgres/interface.h protocol/client.h protocol/command.h protocol/helper.h storage/info.h storage/read.h storage/storage.h storage/write.h version.h
|
||||
|
345
src/db/db.c
345
src/db/db.c
@ -7,12 +7,19 @@ Database Client
|
||||
#include "common/log.h"
|
||||
#include "common/memContext.h"
|
||||
#include "common/object.h"
|
||||
#include "common/wait.h"
|
||||
#include "db/db.h"
|
||||
#include "db/protocol.h"
|
||||
#include "postgres/interface.h"
|
||||
#include "postgres/version.h"
|
||||
#include "protocol/helper.h"
|
||||
#include "version.h"
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Constants
|
||||
***********************************************************************************************************************************/
|
||||
#define PG_BACKUP_ADVISORY_LOCK "12340078987004321"
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Object type
|
||||
***********************************************************************************************************************************/
|
||||
@ -231,6 +238,195 @@ dbOpen(Db *this)
|
||||
FUNCTION_LOG_RETURN_VOID();
|
||||
}
|
||||
|
||||
/**********************************************************************************************************************************/
|
||||
// Helper to build start backup query
|
||||
static String *
|
||||
dbBackupStartQuery(unsigned int pgVersion, bool startFast)
|
||||
{
|
||||
FUNCTION_TEST_BEGIN();
|
||||
FUNCTION_TEST_PARAM(UINT, pgVersion);
|
||||
FUNCTION_TEST_PARAM(BOOL, startFast);
|
||||
FUNCTION_TEST_END();
|
||||
|
||||
// Build query to return start lsn and WAL segment name
|
||||
String *result = strNewFmt(
|
||||
"select lsn::text as lsn,\n"
|
||||
" pg_catalog.pg_%sfile_name(lsn)::text as wal_segment_name\n"
|
||||
" from pg_catalog.pg_start_backup('" PROJECT_NAME " backup started at ' || current_timestamp",
|
||||
strPtr(pgWalName(pgVersion)));
|
||||
|
||||
// Start backup after immediate checkpoint
|
||||
if (startFast)
|
||||
{
|
||||
strCatFmt(result, ", true");
|
||||
}
|
||||
// Else start backup at the next scheduled checkpoint
|
||||
else if (pgVersion >= PG_VERSION_84)
|
||||
strCatFmt(result, ", false");
|
||||
|
||||
// Use non-exclusive backup mode when available
|
||||
if (pgVersion >= PG_VERSION_96)
|
||||
strCatFmt(result, ", false");
|
||||
|
||||
// Complete query
|
||||
strCatFmt(result, ") as lsn");
|
||||
|
||||
FUNCTION_TEST_RETURN(result);
|
||||
}
|
||||
|
||||
#define FUNCTION_LOG_DB_BACKUP_START_RESULT_TYPE \
|
||||
DbBackupStartResult
|
||||
#define FUNCTION_LOG_DB_BACKUP_START_RESULT_FORMAT(value, buffer, bufferSize) \
|
||||
objToLog(&value, "DbBackupStartResult", buffer, bufferSize)
|
||||
|
||||
DbBackupStartResult
|
||||
dbBackupStart(Db *this, bool startFast, bool stopAuto)
|
||||
{
|
||||
FUNCTION_LOG_BEGIN(logLevelDebug);
|
||||
FUNCTION_LOG_PARAM(DB, this);
|
||||
FUNCTION_LOG_PARAM(BOOL, startFast);
|
||||
FUNCTION_LOG_PARAM(BOOL, stopAuto);
|
||||
FUNCTION_LOG_END();
|
||||
|
||||
ASSERT(this != NULL);
|
||||
|
||||
DbBackupStartResult result = {.lsn = NULL};
|
||||
|
||||
MEM_CONTEXT_TEMP_BEGIN()
|
||||
{
|
||||
// Acquire the backup advisory lock to make sure that backups are not running from multiple backup servers against the same
|
||||
// database cluster. This lock helps make the stop-auto option safe.
|
||||
if (!varBool(dbQueryColumn(this, STRDEF("select pg_catalog.pg_try_advisory_lock(" PG_BACKUP_ADVISORY_LOCK ")::bool"))))
|
||||
{
|
||||
THROW(
|
||||
LockAcquireError,
|
||||
"unable to acquire " PROJECT_NAME " advisory lock\n"
|
||||
"HINT: is another " PROJECT_NAME " backup already running on this cluster?");
|
||||
}
|
||||
|
||||
// If stop-auto is enabled check for a running backup
|
||||
if (stopAuto)
|
||||
{
|
||||
// This feature is not supported for PostgreSQL >= 9.6 since backups are run in non-exclusive mode
|
||||
CHECK(dbPgVersion(this) >= PG_VERSION_93 && dbPgVersion(this) < PG_VERSION_96);
|
||||
|
||||
if (varBool(dbQueryColumn(this, STRDEF("select pg_catalog.pg_is_in_backup()::bool"))))
|
||||
{
|
||||
LOG_WARN(
|
||||
"the cluster is already in backup mode but no " PROJECT_NAME " backup process is running."
|
||||
" pg_stop_backup() will be called so a new backup can be started.");
|
||||
|
||||
dbBackupStop(this);
|
||||
}
|
||||
}
|
||||
|
||||
// Start backup
|
||||
VariantList *row = dbQueryRow(this, dbBackupStartQuery(dbPgVersion(this), startFast));
|
||||
|
||||
// Return results
|
||||
memContextSwitch(MEM_CONTEXT_OLD());
|
||||
result.lsn = strDup(varStr(varLstGet(row, 0)));
|
||||
result.walSegmentName = strDup(varStr(varLstGet(row, 1)));
|
||||
memContextSwitch(MEM_CONTEXT_TEMP());
|
||||
}
|
||||
MEM_CONTEXT_TEMP_END();
|
||||
|
||||
FUNCTION_LOG_RETURN(DB_BACKUP_START_RESULT, result);
|
||||
}
|
||||
/**********************************************************************************************************************************/
|
||||
// Helper to build stop backup query
|
||||
static String *
|
||||
dbBackupStopQuery(unsigned int pgVersion)
|
||||
{
|
||||
FUNCTION_TEST_BEGIN();
|
||||
FUNCTION_TEST_PARAM(UINT, pgVersion);
|
||||
FUNCTION_TEST_END();
|
||||
|
||||
// Build query to return start lsn and WAL segment name
|
||||
String *result = strNewFmt(
|
||||
"select lsn::text as lsn,\n"
|
||||
" pg_catalog.pg_%sfile_name(lsn)::text as wal_segment_name",
|
||||
strPtr(pgWalName(pgVersion)));
|
||||
|
||||
// For PostgreSQL >= 9.6 the backup label and tablespace map are returned from pg_stop_backup
|
||||
if (pgVersion >= PG_VERSION_96)
|
||||
{
|
||||
strCat(
|
||||
result,
|
||||
",\n"
|
||||
" labelfile::text as backuplabel_file,\n"
|
||||
" spcmapfile::text as tablespacemap_file");
|
||||
}
|
||||
|
||||
// Build stop backup function
|
||||
strCat(
|
||||
result,
|
||||
"\n"
|
||||
" from pg_catalog.pg_stop_backup(");
|
||||
|
||||
// Use non-exclusive backup mode when available
|
||||
if (pgVersion >= PG_VERSION_96)
|
||||
strCatFmt(result, "false");
|
||||
|
||||
// Disable archive checking in pg_stop_backup() since we do this elsewhere
|
||||
if (pgVersion >= PG_VERSION_10)
|
||||
strCatFmt(result, ", false");
|
||||
|
||||
// Complete query
|
||||
strCatFmt(result, ")");
|
||||
|
||||
if (pgVersion < PG_VERSION_96)
|
||||
strCatFmt(result, " as lsn");
|
||||
|
||||
FUNCTION_TEST_RETURN(result);
|
||||
}
|
||||
|
||||
#define FUNCTION_LOG_DB_BACKUP_STOP_RESULT_TYPE \
|
||||
DbBackupStopResult
|
||||
#define FUNCTION_LOG_DB_BACKUP_STOP_RESULT_FORMAT(value, buffer, bufferSize) \
|
||||
objToLog(&value, "DbBackupStopResult", buffer, bufferSize)
|
||||
|
||||
DbBackupStopResult
|
||||
dbBackupStop(Db *this)
|
||||
{
|
||||
FUNCTION_LOG_BEGIN(logLevelDebug);
|
||||
FUNCTION_LOG_PARAM(DB, this);
|
||||
FUNCTION_LOG_END();
|
||||
|
||||
ASSERT(this != NULL);
|
||||
|
||||
DbBackupStopResult result = {.lsn = NULL};
|
||||
|
||||
MEM_CONTEXT_TEMP_BEGIN()
|
||||
{
|
||||
// Stop backup
|
||||
VariantList *row = dbQueryRow(this, dbBackupStopQuery(dbPgVersion(this)));
|
||||
|
||||
// Check if the tablespace map is empty
|
||||
bool tablespaceMapEmpty =
|
||||
dbPgVersion(this) >= PG_VERSION_96 ? strSize(strTrim(strDup(varStr(varLstGet(row, 3))))) == 0 : false;
|
||||
|
||||
// Return results
|
||||
memContextSwitch(MEM_CONTEXT_OLD());
|
||||
|
||||
result.lsn = strDup(varStr(varLstGet(row, 0)));
|
||||
result.walSegmentName = strDup(varStr(varLstGet(row, 1)));
|
||||
|
||||
if (dbPgVersion(this) >= PG_VERSION_96)
|
||||
{
|
||||
result.backupLabel = strDup(varStr(varLstGet(row, 2)));
|
||||
|
||||
if (!tablespaceMapEmpty)
|
||||
result.tablespaceMap = strDup(varStr(varLstGet(row, 3)));
|
||||
}
|
||||
|
||||
memContextSwitch(MEM_CONTEXT_TEMP());
|
||||
}
|
||||
MEM_CONTEXT_TEMP_END();
|
||||
|
||||
FUNCTION_LOG_RETURN(DB_BACKUP_STOP_RESULT, result);
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Is this instance a standby?
|
||||
***********************************************************************************************************************************/
|
||||
@ -253,6 +449,155 @@ dbIsStandby(Db *this)
|
||||
FUNCTION_LOG_RETURN(BOOL, result);
|
||||
}
|
||||
|
||||
/**********************************************************************************************************************************/
|
||||
VariantList *
|
||||
dbList(Db *this)
|
||||
{
|
||||
FUNCTION_LOG_BEGIN(logLevelDebug);
|
||||
FUNCTION_LOG_PARAM(DB, this);
|
||||
FUNCTION_LOG_END();
|
||||
|
||||
FUNCTION_LOG_RETURN(
|
||||
VARIANT_LIST, dbQuery(this, STRDEF("select oid::oid, datname::text, datlastsysoid::oid from pg_catalog.pg_database")));
|
||||
}
|
||||
|
||||
/**********************************************************************************************************************************/
|
||||
void
|
||||
dbReplayWait(Db *this, const String *targetLsn, TimeMSec timeout)
|
||||
{
|
||||
FUNCTION_LOG_BEGIN(logLevelDebug);
|
||||
FUNCTION_LOG_PARAM(DB, this);
|
||||
FUNCTION_LOG_PARAM(STRING, targetLsn);
|
||||
FUNCTION_LOG_PARAM(TIME_MSEC, timeout);
|
||||
FUNCTION_LOG_END();
|
||||
|
||||
ASSERT(this != NULL);
|
||||
ASSERT(targetLsn != NULL);
|
||||
ASSERT(timeout > 0);
|
||||
|
||||
MEM_CONTEXT_TEMP_BEGIN()
|
||||
{
|
||||
// Loop until lsn has been reached or timeout
|
||||
Wait *wait = waitNew(timeout);
|
||||
bool targetReached = false;
|
||||
const char *lsnName = strPtr(pgLsnName(dbPgVersion(this)));
|
||||
const String *replayLsnFunction = strNewFmt(
|
||||
"pg_catalog.pg_last_%s_replay_%s()", strPtr(pgWalName(dbPgVersion(this))), lsnName);
|
||||
const String *replayLsn = NULL;
|
||||
|
||||
do
|
||||
{
|
||||
// Build the query
|
||||
String *query = strNewFmt(
|
||||
"select replayLsn::text,\n"
|
||||
" (replayLsn > '%s')::bool as targetReached",
|
||||
strPtr(targetLsn));
|
||||
|
||||
if (replayLsn != NULL)
|
||||
{
|
||||
strCatFmt(
|
||||
query,
|
||||
",\n"
|
||||
" (replayLsn > '%s')::bool as replayProgress", strPtr(replayLsn));
|
||||
}
|
||||
|
||||
strCatFmt(
|
||||
query,
|
||||
"\n"
|
||||
" from %s as replayLsn",
|
||||
strPtr(replayLsnFunction));
|
||||
|
||||
// Execute the query and get replayLsn
|
||||
VariantList *row = dbQueryRow(this, query);
|
||||
replayLsn = varStr(varLstGet(row, 0));
|
||||
|
||||
// Error when replayLsn is null which indicates that this is not a standby. This should have been sorted out before we
|
||||
// connected but it's possible that the standy was promoted in the meantime.
|
||||
if (replayLsn == NULL)
|
||||
{
|
||||
THROW_FMT(
|
||||
ArchiveTimeoutError,
|
||||
"unable to query replay lsn on the standby using '%s'\n"
|
||||
"HINT: Is this a standby?",
|
||||
strPtr(replayLsnFunction));
|
||||
}
|
||||
|
||||
targetReached = varBool(varLstGet(row, 1));
|
||||
|
||||
// If the target has not been reached but progress is being made then reset the timer
|
||||
if (!targetReached && varLstSize(row) > 2 && varBool(varLstGet(row, 2)))
|
||||
wait = waitNew(timeout);
|
||||
|
||||
protocolKeepAlive();
|
||||
}
|
||||
while (!targetReached && waitMore(wait));
|
||||
|
||||
// Error if a timeout occurred before the target lsn was reached
|
||||
if (!targetReached)
|
||||
{
|
||||
THROW_FMT(
|
||||
ArchiveTimeoutError, "timeout before standby replayed to %s - only reached %s", strPtr(targetLsn),
|
||||
strPtr(replayLsn));
|
||||
}
|
||||
|
||||
// Perform a checkpoint
|
||||
dbExec(this, STRDEF("checkpoint"));
|
||||
|
||||
// On PostgreSQL >= 9.6 the checkpoint location can be verified
|
||||
//
|
||||
// ??? We have seen one instance where this check failed. Is there any chance that the replayed position could be ahead of
|
||||
// the checkpoint recorded in pg_control? It seems possible since it would be safer if the checkpoint in pg_control was
|
||||
// behind rather than ahead, so add a loop to keep checking until the checkpoint has been recorded or timeout.
|
||||
if (dbPgVersion(this) >= PG_VERSION_96)
|
||||
{
|
||||
// Build the query
|
||||
const String *query = strNewFmt(
|
||||
"select (checkpoint_%s > '%s')::bool as targetReached,\n"
|
||||
" checkpoint_%s::text as checkpointLsn\n"
|
||||
" from pg_catalog.pg_control_checkpoint()",
|
||||
lsnName, strPtr(targetLsn), lsnName);
|
||||
|
||||
// Execute query
|
||||
VariantList *row = dbQueryRow(this, query);
|
||||
|
||||
// Verify target was reached
|
||||
if (!varBool(varLstGet(row, 0)))
|
||||
{
|
||||
THROW_FMT(
|
||||
ArchiveTimeoutError,
|
||||
"the checkpoint lsn %s is less than the target lsn %s even though the replay lsn is %s",
|
||||
strPtr(varStr(varLstGet(row, 1))), strPtr(targetLsn), strPtr(replayLsn));
|
||||
}
|
||||
}
|
||||
}
|
||||
MEM_CONTEXT_TEMP_END();
|
||||
|
||||
FUNCTION_LOG_RETURN_VOID();
|
||||
}
|
||||
|
||||
/**********************************************************************************************************************************/
|
||||
VariantList *
|
||||
dbTablespaceList(Db *this)
|
||||
{
|
||||
FUNCTION_LOG_BEGIN(logLevelDebug);
|
||||
FUNCTION_LOG_PARAM(DB, this);
|
||||
FUNCTION_LOG_END();
|
||||
|
||||
FUNCTION_LOG_RETURN(VARIANT_LIST, dbQuery(this, STRDEF("select oid::oid, spcname::text from pg_catalog.pg_tablespace")));
|
||||
}
|
||||
|
||||
/**********************************************************************************************************************************/
|
||||
TimeMSec
|
||||
dbTimeMSec(Db *this)
|
||||
{
|
||||
FUNCTION_LOG_BEGIN(logLevelDebug);
|
||||
FUNCTION_LOG_PARAM(DB, this);
|
||||
FUNCTION_LOG_END();
|
||||
|
||||
FUNCTION_LOG_RETURN(
|
||||
TIME_MSEC, varUInt64Force(dbQueryColumn(this, STRDEF("select (extract(epoch from clock_timestamp()) * 1000)::bigint"))));
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Switch the WAL segment and return the segment that should have been archived
|
||||
***********************************************************************************************************************************/
|
||||
|
34
src/db/db.h
34
src/db/db.h
@ -27,7 +27,41 @@ Db *dbNew(PgClient *client, ProtocolClient *remoteClient, const String *applicat
|
||||
Functions
|
||||
***********************************************************************************************************************************/
|
||||
void dbOpen(Db *this);
|
||||
|
||||
// Start backup and return starting lsn and wal segment name
|
||||
typedef struct DbBackupStartResult
|
||||
{
|
||||
String *lsn;
|
||||
String *walSegmentName;
|
||||
} DbBackupStartResult;
|
||||
|
||||
DbBackupStartResult dbBackupStart(Db *this, bool startFast, bool stopAuto);
|
||||
|
||||
// Stop backup and return starting lsn, wal segment name, backup label, and tablspace map
|
||||
typedef struct DbBackupStopResult
|
||||
{
|
||||
String *lsn;
|
||||
String *walSegmentName;
|
||||
String *backupLabel;
|
||||
String *tablespaceMap;
|
||||
} DbBackupStopResult;
|
||||
|
||||
DbBackupStopResult dbBackupStop(Db *this);
|
||||
|
||||
bool dbIsStandby(Db *this);
|
||||
|
||||
// Get list of databases in the cluster: select oid, datname, datlastsysoid from pg_database
|
||||
VariantList *dbList(Db *this);
|
||||
|
||||
// Waits for replay on the standby to equal the target LSN
|
||||
void dbReplayWait(Db *this, const String *targetLsn, TimeMSec timeout);
|
||||
|
||||
// Epoch time on the PostgreSQL host in ms
|
||||
TimeMSec dbTimeMSec(Db *this);
|
||||
|
||||
// Get list of tablespaces in the cluster: select oid, datname, datlastsysoid from pg_database
|
||||
VariantList *dbTablespaceList(Db *this);
|
||||
|
||||
String *dbWalSwitch(Db *this);
|
||||
void dbClose(Db *this);
|
||||
|
||||
|
@ -572,7 +572,7 @@ unit:
|
||||
test:
|
||||
# ----------------------------------------------------------------------------------------------------------------------------
|
||||
- name: db
|
||||
total: 2
|
||||
total: 3
|
||||
containerReq: true
|
||||
perlReq: true
|
||||
|
||||
|
@ -134,6 +134,346 @@ Macros for defining groups of functions that implement various queries and comma
|
||||
{.session = sessionParam, .function = HRNPQ_CLEAR}, \
|
||||
{.session = sessionParam, .function = HRNPQ_GETRESULT, .resultNull = true}
|
||||
|
||||
#define HRNPQ_MACRO_TIME_QUERY(sessionParam, timeParam) \
|
||||
{.session = sessionParam, \
|
||||
.function = HRNPQ_SENDQUERY, .param = "[\"select (extract(epoch from clock_timestamp()) * 1000)::bigint\"]", \
|
||||
.resultInt = 1}, \
|
||||
{.session = sessionParam, .function = HRNPQ_CONSUMEINPUT}, \
|
||||
{.session = sessionParam, .function = HRNPQ_ISBUSY}, \
|
||||
{.session = sessionParam, .function = HRNPQ_GETRESULT}, \
|
||||
{.session = sessionParam, .function = HRNPQ_RESULTSTATUS, .resultInt = PGRES_TUPLES_OK}, \
|
||||
{.session = sessionParam, .function = HRNPQ_NTUPLES, .resultInt = 1}, \
|
||||
{.session = sessionParam, .function = HRNPQ_NFIELDS, .resultInt = 1}, \
|
||||
{.session = sessionParam, .function = HRNPQ_FTYPE, .param = "[0]", .resultInt = HRNPQ_TYPE_INT}, \
|
||||
{.session = sessionParam, .function = HRNPQ_GETVALUE, .param = "[0,0]", \
|
||||
.resultZ = strPtr(strNewFmt("%" PRId64, (int64_t)(timeParam)))}, \
|
||||
{.session = sessionParam, .function = HRNPQ_CLEAR}, \
|
||||
{.session = sessionParam, .function = HRNPQ_GETRESULT, .resultNull = true}
|
||||
|
||||
#define HRNPQ_MACRO_ADVISORY_LOCK(sessionParam, lockAcquiredParam) \
|
||||
{.session = sessionParam, \
|
||||
.function = HRNPQ_SENDQUERY, .param = "[\"select pg_catalog.pg_try_advisory_lock(12340078987004321)::bool\"]", \
|
||||
.resultInt = 1}, \
|
||||
{.session = sessionParam, .function = HRNPQ_CONSUMEINPUT}, \
|
||||
{.session = sessionParam, .function = HRNPQ_ISBUSY}, \
|
||||
{.session = sessionParam, .function = HRNPQ_GETRESULT}, \
|
||||
{.session = sessionParam, .function = HRNPQ_RESULTSTATUS, .resultInt = PGRES_TUPLES_OK}, \
|
||||
{.session = sessionParam, .function = HRNPQ_NTUPLES, .resultInt = 1}, \
|
||||
{.session = sessionParam, .function = HRNPQ_NFIELDS, .resultInt = 1}, \
|
||||
{.session = sessionParam, .function = HRNPQ_FTYPE, .param = "[0]", .resultInt = HRNPQ_TYPE_BOOL}, \
|
||||
{.session = sessionParam, .function = HRNPQ_GETVALUE, .param = "[0,0]", .resultZ = cvtBoolToConstZ(lockAcquiredParam)}, \
|
||||
{.session = sessionParam, .function = HRNPQ_CLEAR}, \
|
||||
{.session = sessionParam, .function = HRNPQ_GETRESULT, .resultNull = true}
|
||||
|
||||
#define HRNPQ_MACRO_IS_IN_BACKUP(sessionParam, inBackupParam) \
|
||||
{.session = sessionParam, \
|
||||
.function = HRNPQ_SENDQUERY, .param = "[\"select pg_catalog.pg_is_in_backup()::bool\"]", \
|
||||
.resultInt = 1}, \
|
||||
{.session = sessionParam, .function = HRNPQ_CONSUMEINPUT}, \
|
||||
{.session = sessionParam, .function = HRNPQ_ISBUSY}, \
|
||||
{.session = sessionParam, .function = HRNPQ_GETRESULT}, \
|
||||
{.session = sessionParam, .function = HRNPQ_RESULTSTATUS, .resultInt = PGRES_TUPLES_OK}, \
|
||||
{.session = sessionParam, .function = HRNPQ_NTUPLES, .resultInt = 1}, \
|
||||
{.session = sessionParam, .function = HRNPQ_NFIELDS, .resultInt = 1}, \
|
||||
{.session = sessionParam, .function = HRNPQ_FTYPE, .param = "[0]", .resultInt = HRNPQ_TYPE_BOOL}, \
|
||||
{.session = sessionParam, .function = HRNPQ_GETVALUE, .param = "[0,0]", .resultZ = cvtBoolToConstZ(inBackupParam)}, \
|
||||
{.session = sessionParam, .function = HRNPQ_CLEAR}, \
|
||||
{.session = sessionParam, .function = HRNPQ_GETRESULT, .resultNull = true}
|
||||
|
||||
#define HRNPQ_MACRO_START_BACKUP_83(sessionParam, lsnParam, walSegmentNameParam) \
|
||||
{.session = sessionParam, \
|
||||
.function = HRNPQ_SENDQUERY, \
|
||||
.param = \
|
||||
"[\"select lsn::text as lsn,\\n" \
|
||||
" pg_catalog.pg_xlogfile_name(lsn)::text as wal_segment_name\\n" \
|
||||
" from pg_catalog.pg_start_backup('pgBackRest backup started at ' || current_timestamp) as lsn\"]", \
|
||||
.resultInt = 1}, \
|
||||
{.session = sessionParam, .function = HRNPQ_CONSUMEINPUT}, \
|
||||
{.session = sessionParam, .function = HRNPQ_ISBUSY}, \
|
||||
{.session = sessionParam, .function = HRNPQ_GETRESULT}, \
|
||||
{.session = sessionParam, .function = HRNPQ_RESULTSTATUS, .resultInt = PGRES_TUPLES_OK}, \
|
||||
{.session = sessionParam, .function = HRNPQ_NTUPLES, .resultInt = 1}, \
|
||||
{.session = sessionParam, .function = HRNPQ_NFIELDS, .resultInt = 2}, \
|
||||
{.session = sessionParam, .function = HRNPQ_FTYPE, .param = "[0]", .resultInt = HRNPQ_TYPE_TEXT}, \
|
||||
{.session = sessionParam, .function = HRNPQ_FTYPE, .param = "[1]", .resultInt = HRNPQ_TYPE_TEXT}, \
|
||||
{.session = sessionParam, .function = HRNPQ_GETVALUE, .param = "[0,0]", .resultZ = lsnParam}, \
|
||||
{.session = sessionParam, .function = HRNPQ_GETVALUE, .param = "[0,1]", .resultZ = walSegmentNameParam}, \
|
||||
{.session = sessionParam, .function = HRNPQ_CLEAR}, \
|
||||
{.session = sessionParam, .function = HRNPQ_GETRESULT, .resultNull = true}
|
||||
|
||||
#define HRNPQ_MACRO_START_BACKUP_84_95(sessionParam, startFastParam, lsnParam, walSegmentNameParam) \
|
||||
{.session = sessionParam, \
|
||||
.function = HRNPQ_SENDQUERY, \
|
||||
.param = strPtr(strNewFmt( \
|
||||
"[\"select lsn::text as lsn,\\n" \
|
||||
" pg_catalog.pg_xlogfile_name(lsn)::text as wal_segment_name\\n" \
|
||||
" from pg_catalog.pg_start_backup('pgBackRest backup started at ' || current_timestamp, %s) as lsn\"]", \
|
||||
cvtBoolToConstZ(startFastParam))), \
|
||||
.resultInt = 1}, \
|
||||
{.session = sessionParam, .function = HRNPQ_CONSUMEINPUT}, \
|
||||
{.session = sessionParam, .function = HRNPQ_ISBUSY}, \
|
||||
{.session = sessionParam, .function = HRNPQ_GETRESULT}, \
|
||||
{.session = sessionParam, .function = HRNPQ_RESULTSTATUS, .resultInt = PGRES_TUPLES_OK}, \
|
||||
{.session = sessionParam, .function = HRNPQ_NTUPLES, .resultInt = 1}, \
|
||||
{.session = sessionParam, .function = HRNPQ_NFIELDS, .resultInt = 2}, \
|
||||
{.session = sessionParam, .function = HRNPQ_FTYPE, .param = "[0]", .resultInt = HRNPQ_TYPE_TEXT}, \
|
||||
{.session = sessionParam, .function = HRNPQ_FTYPE, .param = "[1]", .resultInt = HRNPQ_TYPE_TEXT}, \
|
||||
{.session = sessionParam, .function = HRNPQ_GETVALUE, .param = "[0,0]", .resultZ = lsnParam}, \
|
||||
{.session = sessionParam, .function = HRNPQ_GETVALUE, .param = "[0,1]", .resultZ = walSegmentNameParam}, \
|
||||
{.session = sessionParam, .function = HRNPQ_CLEAR}, \
|
||||
{.session = sessionParam, .function = HRNPQ_GETRESULT, .resultNull = true}
|
||||
|
||||
#define HRNPQ_MACRO_START_BACKUP_96(sessionParam, startFastParam, lsnParam, walSegmentNameParam) \
|
||||
{.session = sessionParam, \
|
||||
.function = HRNPQ_SENDQUERY, \
|
||||
.param = strPtr(strNewFmt( \
|
||||
"[\"select lsn::text as lsn,\\n" \
|
||||
" pg_catalog.pg_xlogfile_name(lsn)::text as wal_segment_name\\n" \
|
||||
" from pg_catalog.pg_start_backup('pgBackRest backup started at ' || current_timestamp, %s, false) as lsn\"]", \
|
||||
cvtBoolToConstZ(startFastParam))), \
|
||||
.resultInt = 1}, \
|
||||
{.session = sessionParam, .function = HRNPQ_CONSUMEINPUT}, \
|
||||
{.session = sessionParam, .function = HRNPQ_ISBUSY}, \
|
||||
{.session = sessionParam, .function = HRNPQ_GETRESULT}, \
|
||||
{.session = sessionParam, .function = HRNPQ_RESULTSTATUS, .resultInt = PGRES_TUPLES_OK}, \
|
||||
{.session = sessionParam, .function = HRNPQ_NTUPLES, .resultInt = 1}, \
|
||||
{.session = sessionParam, .function = HRNPQ_NFIELDS, .resultInt = 2}, \
|
||||
{.session = sessionParam, .function = HRNPQ_FTYPE, .param = "[0]", .resultInt = HRNPQ_TYPE_TEXT}, \
|
||||
{.session = sessionParam, .function = HRNPQ_FTYPE, .param = "[1]", .resultInt = HRNPQ_TYPE_TEXT}, \
|
||||
{.session = sessionParam, .function = HRNPQ_GETVALUE, .param = "[0,0]", .resultZ = lsnParam}, \
|
||||
{.session = sessionParam, .function = HRNPQ_GETVALUE, .param = "[0,1]", .resultZ = walSegmentNameParam}, \
|
||||
{.session = sessionParam, .function = HRNPQ_CLEAR}, \
|
||||
{.session = sessionParam, .function = HRNPQ_GETRESULT, .resultNull = true}
|
||||
|
||||
#define HRNPQ_MACRO_START_BACKUP_GE_10(sessionParam, startFastParam, lsnParam, walSegmentNameParam) \
|
||||
{.session = sessionParam, \
|
||||
.function = HRNPQ_SENDQUERY, \
|
||||
.param = strPtr(strNewFmt( \
|
||||
"[\"select lsn::text as lsn,\\n" \
|
||||
" pg_catalog.pg_walfile_name(lsn)::text as wal_segment_name\\n" \
|
||||
" from pg_catalog.pg_start_backup('pgBackRest backup started at ' || current_timestamp, %s, false) as lsn\"]", \
|
||||
cvtBoolToConstZ(startFastParam))), \
|
||||
.resultInt = 1}, \
|
||||
{.session = sessionParam, .function = HRNPQ_CONSUMEINPUT}, \
|
||||
{.session = sessionParam, .function = HRNPQ_ISBUSY}, \
|
||||
{.session = sessionParam, .function = HRNPQ_GETRESULT}, \
|
||||
{.session = sessionParam, .function = HRNPQ_RESULTSTATUS, .resultInt = PGRES_TUPLES_OK}, \
|
||||
{.session = sessionParam, .function = HRNPQ_NTUPLES, .resultInt = 1}, \
|
||||
{.session = sessionParam, .function = HRNPQ_NFIELDS, .resultInt = 2}, \
|
||||
{.session = sessionParam, .function = HRNPQ_FTYPE, .param = "[0]", .resultInt = HRNPQ_TYPE_TEXT}, \
|
||||
{.session = sessionParam, .function = HRNPQ_FTYPE, .param = "[1]", .resultInt = HRNPQ_TYPE_TEXT}, \
|
||||
{.session = sessionParam, .function = HRNPQ_GETVALUE, .param = "[0,0]", .resultZ = lsnParam}, \
|
||||
{.session = sessionParam, .function = HRNPQ_GETVALUE, .param = "[0,1]", .resultZ = walSegmentNameParam}, \
|
||||
{.session = sessionParam, .function = HRNPQ_CLEAR}, \
|
||||
{.session = sessionParam, .function = HRNPQ_GETRESULT, .resultNull = true}
|
||||
|
||||
#define HRNPQ_MACRO_STOP_BACKUP_LE_95(sessionParam, lsnParam, walSegmentNameParam) \
|
||||
{.session = sessionParam, \
|
||||
.function = HRNPQ_SENDQUERY, \
|
||||
.param = \
|
||||
"[\"select lsn::text as lsn,\\n" \
|
||||
" pg_catalog.pg_xlogfile_name(lsn)::text as wal_segment_name\\n" \
|
||||
" from pg_catalog.pg_stop_backup() as lsn\"]", \
|
||||
.resultInt = 1}, \
|
||||
{.session = sessionParam, .function = HRNPQ_CONSUMEINPUT}, \
|
||||
{.session = sessionParam, .function = HRNPQ_ISBUSY}, \
|
||||
{.session = sessionParam, .function = HRNPQ_GETRESULT}, \
|
||||
{.session = sessionParam, .function = HRNPQ_RESULTSTATUS, .resultInt = PGRES_TUPLES_OK}, \
|
||||
{.session = sessionParam, .function = HRNPQ_NTUPLES, .resultInt = 1}, \
|
||||
{.session = sessionParam, .function = HRNPQ_NFIELDS, .resultInt = 2}, \
|
||||
{.session = sessionParam, .function = HRNPQ_FTYPE, .param = "[0]", .resultInt = HRNPQ_TYPE_TEXT}, \
|
||||
{.session = sessionParam, .function = HRNPQ_FTYPE, .param = "[1]", .resultInt = HRNPQ_TYPE_TEXT}, \
|
||||
{.session = sessionParam, .function = HRNPQ_GETVALUE, .param = "[0,0]", .resultZ = lsnParam}, \
|
||||
{.session = sessionParam, .function = HRNPQ_GETVALUE, .param = "[0,1]", .resultZ = walSegmentNameParam}, \
|
||||
{.session = sessionParam, .function = HRNPQ_CLEAR}, \
|
||||
{.session = sessionParam, .function = HRNPQ_GETRESULT, .resultNull = true}
|
||||
|
||||
#define HRNPQ_MACRO_STOP_BACKUP_96(sessionParam, lsnParam, walSegmentNameParam, tablespaceMapParam) \
|
||||
{.session = sessionParam, \
|
||||
.function = HRNPQ_SENDQUERY, \
|
||||
.param = \
|
||||
"[\"select lsn::text as lsn,\\n" \
|
||||
" pg_catalog.pg_xlogfile_name(lsn)::text as wal_segment_name,\\n" \
|
||||
" labelfile::text as backuplabel_file,\\n" \
|
||||
" spcmapfile::text as tablespacemap_file\\n" \
|
||||
" from pg_catalog.pg_stop_backup(false)\"]", \
|
||||
.resultInt = 1}, \
|
||||
{.session = sessionParam, .function = HRNPQ_CONSUMEINPUT}, \
|
||||
{.session = sessionParam, .function = HRNPQ_ISBUSY}, \
|
||||
{.session = sessionParam, .function = HRNPQ_GETRESULT}, \
|
||||
{.session = sessionParam, .function = HRNPQ_RESULTSTATUS, .resultInt = PGRES_TUPLES_OK}, \
|
||||
{.session = sessionParam, .function = HRNPQ_NTUPLES, .resultInt = 1}, \
|
||||
{.session = sessionParam, .function = HRNPQ_NFIELDS, .resultInt = 4}, \
|
||||
{.session = sessionParam, .function = HRNPQ_FTYPE, .param = "[0]", .resultInt = HRNPQ_TYPE_TEXT}, \
|
||||
{.session = sessionParam, .function = HRNPQ_FTYPE, .param = "[1]", .resultInt = HRNPQ_TYPE_TEXT}, \
|
||||
{.session = sessionParam, .function = HRNPQ_FTYPE, .param = "[2]", .resultInt = HRNPQ_TYPE_TEXT}, \
|
||||
{.session = sessionParam, .function = HRNPQ_FTYPE, .param = "[3]", .resultInt = HRNPQ_TYPE_TEXT}, \
|
||||
{.session = sessionParam, .function = HRNPQ_GETVALUE, .param = "[0,0]", .resultZ = lsnParam}, \
|
||||
{.session = sessionParam, .function = HRNPQ_GETVALUE, .param = "[0,1]", .resultZ = walSegmentNameParam}, \
|
||||
{.session = sessionParam, .function = HRNPQ_GETVALUE, .param = "[0,2]", .resultZ = "BACKUP_LABEL_DATA"}, \
|
||||
{.session = sessionParam, .function = HRNPQ_GETVALUE, .param = "[0,3]", \
|
||||
.resultZ = tablespaceMapParam ? "TABLESPACE_MAP_DATA" : "\n"}, \
|
||||
{.session = sessionParam, .function = HRNPQ_CLEAR}, \
|
||||
{.session = sessionParam, .function = HRNPQ_GETRESULT, .resultNull = true}
|
||||
|
||||
#define HRNPQ_MACRO_STOP_BACKUP_GE_10(sessionParam, lsnParam, walSegmentNameParam, tablespaceMapParam) \
|
||||
{.session = sessionParam, \
|
||||
.function = HRNPQ_SENDQUERY, \
|
||||
.param = \
|
||||
"[\"select lsn::text as lsn,\\n" \
|
||||
" pg_catalog.pg_walfile_name(lsn)::text as wal_segment_name,\\n" \
|
||||
" labelfile::text as backuplabel_file,\\n" \
|
||||
" spcmapfile::text as tablespacemap_file\\n" \
|
||||
" from pg_catalog.pg_stop_backup(false, false)\"]", \
|
||||
.resultInt = 1}, \
|
||||
{.session = sessionParam, .function = HRNPQ_CONSUMEINPUT}, \
|
||||
{.session = sessionParam, .function = HRNPQ_ISBUSY}, \
|
||||
{.session = sessionParam, .function = HRNPQ_GETRESULT}, \
|
||||
{.session = sessionParam, .function = HRNPQ_RESULTSTATUS, .resultInt = PGRES_TUPLES_OK}, \
|
||||
{.session = sessionParam, .function = HRNPQ_NTUPLES, .resultInt = 1}, \
|
||||
{.session = sessionParam, .function = HRNPQ_NFIELDS, .resultInt = 4}, \
|
||||
{.session = sessionParam, .function = HRNPQ_FTYPE, .param = "[0]", .resultInt = HRNPQ_TYPE_TEXT}, \
|
||||
{.session = sessionParam, .function = HRNPQ_FTYPE, .param = "[1]", .resultInt = HRNPQ_TYPE_TEXT}, \
|
||||
{.session = sessionParam, .function = HRNPQ_FTYPE, .param = "[2]", .resultInt = HRNPQ_TYPE_TEXT}, \
|
||||
{.session = sessionParam, .function = HRNPQ_FTYPE, .param = "[3]", .resultInt = HRNPQ_TYPE_TEXT}, \
|
||||
{.session = sessionParam, .function = HRNPQ_GETVALUE, .param = "[0,0]", .resultZ = lsnParam}, \
|
||||
{.session = sessionParam, .function = HRNPQ_GETVALUE, .param = "[0,1]", .resultZ = walSegmentNameParam}, \
|
||||
{.session = sessionParam, .function = HRNPQ_GETVALUE, .param = "[0,2]", .resultZ = "BACKUP_LABEL_DATA"}, \
|
||||
{.session = sessionParam, .function = HRNPQ_GETVALUE, .param = "[0,3]", \
|
||||
.resultZ = tablespaceMapParam ? "TABLESPACE_MAP_DATA" : "\n"}, \
|
||||
{.session = sessionParam, .function = HRNPQ_CLEAR}, \
|
||||
{.session = sessionParam, .function = HRNPQ_GETRESULT, .resultNull = true}
|
||||
|
||||
#define HRNPQ_MACRO_DATABASE_LIST_1(sessionParam, databaseNameParam) \
|
||||
{.session = sessionParam, \
|
||||
.function = HRNPQ_SENDQUERY, \
|
||||
.param = "[\"select oid::oid, datname::text, datlastsysoid::oid from pg_catalog.pg_database\"]", \
|
||||
.resultInt = 1}, \
|
||||
{.session = sessionParam, .function = HRNPQ_CONSUMEINPUT}, \
|
||||
{.session = sessionParam, .function = HRNPQ_ISBUSY}, \
|
||||
{.session = sessionParam, .function = HRNPQ_GETRESULT}, \
|
||||
{.session = sessionParam, .function = HRNPQ_RESULTSTATUS, .resultInt = PGRES_TUPLES_OK}, \
|
||||
{.session = sessionParam, .function = HRNPQ_NTUPLES, .resultInt = 1}, \
|
||||
{.session = sessionParam, .function = HRNPQ_NFIELDS, .resultInt = 3}, \
|
||||
{.session = sessionParam, .function = HRNPQ_FTYPE, .param = "[0]", .resultInt = HRNPQ_TYPE_INT}, \
|
||||
{.session = sessionParam, .function = HRNPQ_FTYPE, .param = "[1]", .resultInt = HRNPQ_TYPE_TEXT}, \
|
||||
{.session = sessionParam, .function = HRNPQ_FTYPE, .param = "[2]", .resultInt = HRNPQ_TYPE_INT}, \
|
||||
{.session = sessionParam, .function = HRNPQ_GETVALUE, .param = "[0,0]", .resultZ = STRINGIFY(16384)}, \
|
||||
{.session = sessionParam, .function = HRNPQ_GETVALUE, .param = "[0,1]", .resultZ = databaseNameParam}, \
|
||||
{.session = sessionParam, .function = HRNPQ_GETVALUE, .param = "[0,2]", .resultZ = STRINGIFY(13777)}, \
|
||||
{.session = sessionParam, .function = HRNPQ_CLEAR}, \
|
||||
{.session = sessionParam, .function = HRNPQ_GETRESULT, .resultNull = true}
|
||||
|
||||
#define HRNPQ_MACRO_TABLESPACE_LIST_0(sessionParam) \
|
||||
{.session = sessionParam, \
|
||||
.function = HRNPQ_SENDQUERY, .param = "[\"select oid::oid, spcname::text from pg_catalog.pg_tablespace\"]", \
|
||||
.resultInt = 1}, \
|
||||
{.session = sessionParam, .function = HRNPQ_CONSUMEINPUT}, \
|
||||
{.session = sessionParam, .function = HRNPQ_ISBUSY}, \
|
||||
{.session = sessionParam, .function = HRNPQ_GETRESULT}, \
|
||||
{.session = sessionParam, .function = HRNPQ_RESULTSTATUS, .resultInt = PGRES_TUPLES_OK}, \
|
||||
{.session = sessionParam, .function = HRNPQ_NTUPLES, .resultInt = 0}, \
|
||||
{.session = sessionParam, .function = HRNPQ_NFIELDS, .resultInt = 2}, \
|
||||
{.session = sessionParam, .function = HRNPQ_FTYPE, .param = "[0]", .resultInt = HRNPQ_TYPE_INT}, \
|
||||
{.session = sessionParam, .function = HRNPQ_FTYPE, .param = "[1]", .resultInt = HRNPQ_TYPE_TEXT}, \
|
||||
{.session = sessionParam, .function = HRNPQ_CLEAR}, \
|
||||
{.session = sessionParam, .function = HRNPQ_GETRESULT, .resultNull = true}
|
||||
|
||||
#define HRNPQ_MACRO_TABLESPACE_LIST_1(sessionParam, id1Param, name1Param) \
|
||||
{.session = sessionParam, \
|
||||
.function = HRNPQ_SENDQUERY, .param = "[\"select oid::oid, spcname::text from pg_catalog.pg_tablespace\"]", \
|
||||
.resultInt = 1}, \
|
||||
{.session = sessionParam, .function = HRNPQ_CONSUMEINPUT}, \
|
||||
{.session = sessionParam, .function = HRNPQ_ISBUSY}, \
|
||||
{.session = sessionParam, .function = HRNPQ_GETRESULT}, \
|
||||
{.session = sessionParam, .function = HRNPQ_RESULTSTATUS, .resultInt = PGRES_TUPLES_OK}, \
|
||||
{.session = sessionParam, .function = HRNPQ_NTUPLES, .resultInt = 1}, \
|
||||
{.session = sessionParam, .function = HRNPQ_NFIELDS, .resultInt = 2}, \
|
||||
{.session = sessionParam, .function = HRNPQ_FTYPE, .param = "[0]", .resultInt = HRNPQ_TYPE_INT}, \
|
||||
{.session = sessionParam, .function = HRNPQ_FTYPE, .param = "[1]", .resultInt = HRNPQ_TYPE_TEXT}, \
|
||||
{.session = sessionParam, .function = HRNPQ_GETVALUE, .param = "[0,0]", .resultZ = strPtr(strNewFmt("%d", id1Param))}, \
|
||||
{.session = sessionParam, .function = HRNPQ_GETVALUE, .param = "[0,1]", .resultZ = name1Param}, \
|
||||
{.session = sessionParam, .function = HRNPQ_CLEAR}, \
|
||||
{.session = sessionParam, .function = HRNPQ_GETRESULT, .resultNull = true}
|
||||
|
||||
#define HRNPQ_MACRO_CHECKPOINT(sessionParam) \
|
||||
{.session = sessionParam, .function = HRNPQ_SENDQUERY, .param = "[\"checkpoint\"]", .resultInt = 1}, \
|
||||
{.session = sessionParam, .function = HRNPQ_CONSUMEINPUT}, \
|
||||
{.session = sessionParam, .function = HRNPQ_ISBUSY}, \
|
||||
{.session = sessionParam, .function = HRNPQ_GETRESULT}, \
|
||||
{.session = sessionParam, .function = HRNPQ_RESULTSTATUS, .resultInt = PGRES_COMMAND_OK}, \
|
||||
{.session = sessionParam, .function = HRNPQ_CLEAR}, \
|
||||
{.session = sessionParam, .function = HRNPQ_GETRESULT, .resultNull = true}
|
||||
|
||||
#define HRNPQ_MACRO_REPLAY_TARGET_REACHED( \
|
||||
sessionParam, walNameParam, lsnNameParam, targetLsnParam, targetReachedParam, replayLsnParam) \
|
||||
{.session = sessionParam, \
|
||||
.function = HRNPQ_SENDQUERY, \
|
||||
.param = \
|
||||
"[\"select replayLsn::text,\\n" \
|
||||
" (replayLsn > '" targetLsnParam "')::bool as targetReached\\n" \
|
||||
" from pg_catalog.pg_last_" walNameParam "_replay_" lsnNameParam "() as replayLsn\"]", \
|
||||
.resultInt = 1}, \
|
||||
{.session = sessionParam, .function = HRNPQ_CONSUMEINPUT}, \
|
||||
{.session = sessionParam, .function = HRNPQ_ISBUSY}, \
|
||||
{.session = sessionParam, .function = HRNPQ_GETRESULT}, \
|
||||
{.session = sessionParam, .function = HRNPQ_RESULTSTATUS, .resultInt = PGRES_TUPLES_OK}, \
|
||||
{.session = sessionParam, .function = HRNPQ_NTUPLES, .resultInt = 1}, \
|
||||
{.session = sessionParam, .function = HRNPQ_NFIELDS, .resultInt = 2}, \
|
||||
{.session = sessionParam, .function = HRNPQ_FTYPE, .param = "[0]", .resultInt = HRNPQ_TYPE_TEXT}, \
|
||||
{.session = sessionParam, .function = HRNPQ_FTYPE, .param = "[1]", .resultInt = HRNPQ_TYPE_BOOL}, \
|
||||
{.session = sessionParam, .function = HRNPQ_GETVALUE, .param = "[0,0]", .resultZ = replayLsnParam}, \
|
||||
{.session = sessionParam, .function = HRNPQ_GETVALUE, .param = "[0,1]", .resultZ = cvtBoolToConstZ(targetReachedParam)}, \
|
||||
{.session = sessionParam, .function = HRNPQ_CLEAR}, \
|
||||
{.session = sessionParam, .function = HRNPQ_GETRESULT, .resultNull = true}
|
||||
|
||||
#define HRNPQ_MACRO_REPLAY_TARGET_REACHED_LE_96(sessionParam, targetLsnParam, targetReachedParam, reachedLsnParam) \
|
||||
HRNPQ_MACRO_REPLAY_TARGET_REACHED(sessionParam, "xlog", "location", targetLsnParam, targetReachedParam, reachedLsnParam)
|
||||
|
||||
#define HRNPQ_MACRO_REPLAY_TARGET_REACHED_GE_10(sessionParam, targetLsnParam, targetReachedParam, reachedLsnParam) \
|
||||
HRNPQ_MACRO_REPLAY_TARGET_REACHED(sessionParam, "wal", "lsn", targetLsnParam, targetReachedParam, reachedLsnParam)
|
||||
|
||||
#define HRNPQ_MACRO_CHECKPOINT_TARGET_REACHED(sessionParam, lsnNameParam, targetLsnParam, targetReachedParam, checkpointLsnParam) \
|
||||
{.session = sessionParam, \
|
||||
.function = HRNPQ_SENDQUERY, \
|
||||
.param = \
|
||||
"[\"select (checkpoint_" lsnNameParam " > '" targetLsnParam "')::bool as targetReached,\\n" \
|
||||
" checkpoint_" lsnNameParam "::text as checkpointLsn\\n" \
|
||||
" from pg_catalog.pg_control_checkpoint()\"]", \
|
||||
.resultInt = 1}, \
|
||||
{.session = sessionParam, .function = HRNPQ_CONSUMEINPUT}, \
|
||||
{.session = sessionParam, .function = HRNPQ_ISBUSY}, \
|
||||
{.session = sessionParam, .function = HRNPQ_GETRESULT}, \
|
||||
{.session = sessionParam, .function = HRNPQ_RESULTSTATUS, .resultInt = PGRES_TUPLES_OK}, \
|
||||
{.session = sessionParam, .function = HRNPQ_NTUPLES, .resultInt = 1}, \
|
||||
{.session = sessionParam, .function = HRNPQ_NFIELDS, .resultInt = 2}, \
|
||||
{.session = sessionParam, .function = HRNPQ_FTYPE, .param = "[0]", .resultInt = HRNPQ_TYPE_BOOL}, \
|
||||
{.session = sessionParam, .function = HRNPQ_FTYPE, .param = "[1]", .resultInt = HRNPQ_TYPE_TEXT}, \
|
||||
{.session = sessionParam, .function = HRNPQ_GETVALUE, .param = "[0,0]", .resultZ = cvtBoolToConstZ(targetReachedParam)}, \
|
||||
{.session = sessionParam, .function = HRNPQ_GETVALUE, .param = "[0,1]", .resultZ = checkpointLsnParam}, \
|
||||
{.session = sessionParam, .function = HRNPQ_CLEAR}, \
|
||||
{.session = sessionParam, .function = HRNPQ_GETRESULT, .resultNull = true}
|
||||
|
||||
#define HRNPQ_MACRO_CHECKPOINT_TARGET_REACHED_96(sessionParam, targetLsnParam, targetReachedParam, checkpointLsnParam) \
|
||||
HRNPQ_MACRO_CHECKPOINT_TARGET_REACHED(sessionParam, "location", targetLsnParam, targetReachedParam, checkpointLsnParam)
|
||||
|
||||
#define HRNPQ_MACRO_CHECKPOINT_TARGET_REACHED_GE_10(sessionParam, targetLsnParam, targetReachedParam, checkpointLsnParam) \
|
||||
HRNPQ_MACRO_CHECKPOINT_TARGET_REACHED(sessionParam, "lsn", targetLsnParam, targetReachedParam, checkpointLsnParam)
|
||||
|
||||
#define HRNPQ_MACRO_REPLAY_WAIT_LE_95(sessionParam, targetLsnParam) \
|
||||
HRNPQ_MACRO_REPLAY_TARGET_REACHED_LE_96(sessionParam, targetLsnParam, true, "X/X"), \
|
||||
HRNPQ_MACRO_CHECKPOINT(sessionParam)
|
||||
|
||||
#define HRNPQ_MACRO_REPLAY_WAIT_96(sessionParam, targetLsnParam) \
|
||||
HRNPQ_MACRO_REPLAY_TARGET_REACHED_LE_96(sessionParam, targetLsnParam, true, "X/X"), \
|
||||
HRNPQ_MACRO_CHECKPOINT(sessionParam), \
|
||||
HRNPQ_MACRO_CHECKPOINT_TARGET_REACHED_96(sessionParam, targetLsnParam, true, "X/X")
|
||||
|
||||
#define HRNPQ_MACRO_REPLAY_WAIT_GE_10(sessionParam, targetLsnParam) \
|
||||
HRNPQ_MACRO_REPLAY_TARGET_REACHED_GE_10(sessionParam, targetLsnParam, true, "X/X"), \
|
||||
HRNPQ_MACRO_CHECKPOINT(sessionParam), \
|
||||
HRNPQ_MACRO_CHECKPOINT_TARGET_REACHED_GE_10(sessionParam, targetLsnParam, true, "X/X")
|
||||
|
||||
#define HRNPQ_MACRO_CLOSE(sessionParam) \
|
||||
{.session = sessionParam, .function = HRNPQ_FINISH}
|
||||
|
||||
@ -143,18 +483,22 @@ Macros for defining groups of functions that implement various queries and comma
|
||||
/***********************************************************************************************************************************
|
||||
Macros to simplify dbOpen() for specific database versions
|
||||
***********************************************************************************************************************************/
|
||||
#define HRNPQ_MACRO_OPEN_84(sessionParam, connectParam, pgPathParam, archiveMode, archiveCommand) \
|
||||
#define HRNPQ_MACRO_OPEN_LE_91(sessionParam, connectParam, pgVersion, pgPathParam, archiveMode, archiveCommand) \
|
||||
HRNPQ_MACRO_OPEN(sessionParam, connectParam), \
|
||||
HRNPQ_MACRO_SET_SEARCH_PATH(sessionParam), \
|
||||
HRNPQ_MACRO_VALIDATE_QUERY(sessionParam, PG_VERSION_84, pgPathParam, archiveMode, archiveCommand)
|
||||
HRNPQ_MACRO_VALIDATE_QUERY(sessionParam, pgVersion, pgPathParam, archiveMode, archiveCommand)
|
||||
|
||||
#define HRNPQ_MACRO_OPEN_92(sessionParam, connectParam, pgPathParam, standbyParam, archiveMode, archiveCommand) \
|
||||
#define HRNPQ_MACRO_OPEN_GE_92(sessionParam, connectParam, pgVersion, pgPathParam, standbyParam, archiveMode, archiveCommand) \
|
||||
HRNPQ_MACRO_OPEN(sessionParam, connectParam), \
|
||||
HRNPQ_MACRO_SET_SEARCH_PATH(sessionParam), \
|
||||
HRNPQ_MACRO_VALIDATE_QUERY(sessionParam, PG_VERSION_92, pgPathParam, archiveMode, archiveCommand), \
|
||||
HRNPQ_MACRO_VALIDATE_QUERY(sessionParam, pgVersion, pgPathParam, archiveMode, archiveCommand), \
|
||||
HRNPQ_MACRO_SET_APPLICATION_NAME(sessionParam), \
|
||||
HRNPQ_MACRO_IS_STANDBY_QUERY(sessionParam, standbyParam)
|
||||
|
||||
// ??? This is really just a special case of the above and should be replaced by it
|
||||
#define HRNPQ_MACRO_OPEN_92(sessionParam, connectParam, pgPathParam, standbyParam, archiveMode, archiveCommand) \
|
||||
HRNPQ_MACRO_OPEN_GE_92(sessionParam, connectParam, PG_VERSION_92, pgPathParam, standbyParam, archiveMode, archiveCommand)
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Data type constants
|
||||
***********************************************************************************************************************************/
|
||||
|
@ -8,6 +8,42 @@ Test Database
|
||||
|
||||
#include "common/io/handleRead.h"
|
||||
#include "common/io/handleWrite.h"
|
||||
#include "common/type/json.h"
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Macro to check that replay is making progress -- this does not seem useful enough to be included in the pq harness header
|
||||
***********************************************************************************************************************************/
|
||||
#define HRNPQ_MACRO_REPLAY_TARGET_REACHED_PROGRESS( \
|
||||
sessionParam, walNameParam, lsnNameParam, targetLsnParam, targetReachedParam, replayLsnParam, replayLastLsnParam, \
|
||||
replayProgressParam, sleepParam) \
|
||||
{.session = sessionParam, \
|
||||
.function = HRNPQ_SENDQUERY, \
|
||||
.param = \
|
||||
"[\"select replayLsn::text,\\n" \
|
||||
" (replayLsn > '" targetLsnParam "')::bool as targetReached,\\n" \
|
||||
" (replayLsn > '" replayLastLsnParam "')::bool as replayProgress\\n" \
|
||||
" from pg_catalog.pg_last_" walNameParam "_replay_" lsnNameParam "() as replayLsn\"]", \
|
||||
.resultInt = 1, .sleep = sleepParam}, \
|
||||
{.session = sessionParam, .function = HRNPQ_CONSUMEINPUT}, \
|
||||
{.session = sessionParam, .function = HRNPQ_ISBUSY}, \
|
||||
{.session = sessionParam, .function = HRNPQ_GETRESULT}, \
|
||||
{.session = sessionParam, .function = HRNPQ_RESULTSTATUS, .resultInt = PGRES_TUPLES_OK}, \
|
||||
{.session = sessionParam, .function = HRNPQ_NTUPLES, .resultInt = 1}, \
|
||||
{.session = sessionParam, .function = HRNPQ_NFIELDS, .resultInt = 3}, \
|
||||
{.session = sessionParam, .function = HRNPQ_FTYPE, .param = "[0]", .resultInt = HRNPQ_TYPE_TEXT}, \
|
||||
{.session = sessionParam, .function = HRNPQ_FTYPE, .param = "[1]", .resultInt = HRNPQ_TYPE_BOOL}, \
|
||||
{.session = sessionParam, .function = HRNPQ_FTYPE, .param = "[2]", .resultInt = HRNPQ_TYPE_BOOL}, \
|
||||
{.session = sessionParam, .function = HRNPQ_GETVALUE, .param = "[0,0]", .resultZ = replayLsnParam}, \
|
||||
{.session = sessionParam, .function = HRNPQ_GETVALUE, .param = "[0,1]", .resultZ = cvtBoolToConstZ(targetReachedParam)}, \
|
||||
{.session = sessionParam, .function = HRNPQ_GETVALUE, .param = "[0,2]", .resultZ = cvtBoolToConstZ(replayProgressParam)}, \
|
||||
{.session = sessionParam, .function = HRNPQ_CLEAR}, \
|
||||
{.session = sessionParam, .function = HRNPQ_GETRESULT, .resultNull = true}
|
||||
|
||||
#define HRNPQ_MACRO_REPLAY_TARGET_REACHED_PROGRESS_GE_10( \
|
||||
sessionParam, targetLsnParam, targetReachedParam, replayLsnParam, replayLastLsnParam, replayProgressParam, sleepParam) \
|
||||
HRNPQ_MACRO_REPLAY_TARGET_REACHED_PROGRESS( \
|
||||
sessionParam, "wal", "lsn", targetLsnParam, targetReachedParam, replayLsnParam, replayLastLsnParam, replayProgressParam, \
|
||||
sleepParam)
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Test Run
|
||||
@ -86,7 +122,7 @@ testRun(void)
|
||||
TEST_RESULT_VOID(dbOpen(db), "open db");
|
||||
TEST_RESULT_VOID(dbFree(db), "free db");
|
||||
|
||||
// Open the database, but don't free it so the server is force to do it on shutdown
|
||||
// Open the database, but don't free it so the server is forced to do it on shutdown
|
||||
TEST_ASSIGN(db, dbNew(NULL, client, strNew("test")), "create db");
|
||||
TEST_RESULT_VOID(dbOpen(db), "open db");
|
||||
TEST_RESULT_STR(strPtr(dbWalSwitch(db)), "000000030000000200000003", " wal switch");
|
||||
@ -99,6 +135,308 @@ testRun(void)
|
||||
HARNESS_FORK_END();
|
||||
}
|
||||
|
||||
// *****************************************************************************************************************************
|
||||
if (testBegin("dbBackupStart(), dbBackupStop(), dbTime(), dbList(), dbTablespaceList(), and dbReplayWait()"))
|
||||
{
|
||||
StringList *argList = strLstNew();
|
||||
strLstAddZ(argList, "--stanza=test1");
|
||||
strLstAddZ(argList, "--repo1-retention-full=1");
|
||||
strLstAddZ(argList, "--pg1-path=/pg1");
|
||||
harnessCfgLoad(cfgCmdBackup, argList);
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("PostgreSQL 8.3 start backup with no start fast");
|
||||
|
||||
harnessPqScriptSet((HarnessPq [])
|
||||
{
|
||||
// Connect to primary
|
||||
HRNPQ_MACRO_OPEN_LE_91(1, "dbname='postgres' port=5432", PG_VERSION_83, "/pg1", NULL, NULL),
|
||||
|
||||
// Get advisory lock
|
||||
HRNPQ_MACRO_ADVISORY_LOCK(1, true),
|
||||
|
||||
// Start backup with no start fast
|
||||
HRNPQ_MACRO_START_BACKUP_83(1, "1/1", "000000010000000100000001"),
|
||||
|
||||
// Close primary
|
||||
HRNPQ_MACRO_CLOSE(1),
|
||||
|
||||
HRNPQ_MACRO_DONE()
|
||||
});
|
||||
|
||||
DbGetResult db = {.primaryId = 0};
|
||||
TEST_ASSIGN(db, dbGet(true, true, false), "get primary");
|
||||
|
||||
TEST_RESULT_STR_Z(dbBackupStart(db.primary, false, false).lsn, "1/1", "start backup");
|
||||
|
||||
TEST_RESULT_VOID(dbFree(db.primary), "free primary");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("PostgreSQL 9.5 start/stop backup");
|
||||
|
||||
harnessPqScriptSet((HarnessPq [])
|
||||
{
|
||||
// Connect to primary
|
||||
HRNPQ_MACRO_OPEN_GE_92(1, "dbname='postgres' port=5432", PG_VERSION_95, "/pg1", false, NULL, NULL),
|
||||
|
||||
// Get start time
|
||||
HRNPQ_MACRO_TIME_QUERY(1, 1000),
|
||||
|
||||
// Start backup errors on advisory lock
|
||||
HRNPQ_MACRO_ADVISORY_LOCK(1, false),
|
||||
|
||||
// Start backup
|
||||
HRNPQ_MACRO_ADVISORY_LOCK(1, true),
|
||||
HRNPQ_MACRO_IS_IN_BACKUP(1, false),
|
||||
HRNPQ_MACRO_START_BACKUP_84_95(1, false, "2/3", "000000010000000200000003"),
|
||||
HRNPQ_MACRO_DATABASE_LIST_1(1, "test1"),
|
||||
HRNPQ_MACRO_TABLESPACE_LIST_0(1),
|
||||
|
||||
// Stop backup
|
||||
HRNPQ_MACRO_STOP_BACKUP_LE_95(1, "2/4", "000000010000000200000004"),
|
||||
|
||||
// Close primary
|
||||
HRNPQ_MACRO_CLOSE(1),
|
||||
|
||||
HRNPQ_MACRO_DONE()
|
||||
});
|
||||
|
||||
TEST_ASSIGN(db, dbGet(true, true, false), "get primary");
|
||||
|
||||
TEST_RESULT_UINT(dbTimeMSec(db.primary), 1000, "check time");
|
||||
|
||||
TEST_ERROR(
|
||||
dbBackupStart(db.primary, false, false), LockAcquireError,
|
||||
"unable to acquire pgBackRest advisory lock\n"
|
||||
"HINT: is another pgBackRest backup already running on this cluster?");
|
||||
|
||||
DbBackupStartResult backupStartResult = {.lsn = NULL};
|
||||
TEST_ASSIGN(backupStartResult, dbBackupStart(db.primary, false, true), "start backup");
|
||||
TEST_RESULT_STR_Z(backupStartResult.lsn, "2/3", "check lsn");
|
||||
TEST_RESULT_STR_Z(backupStartResult.walSegmentName, "000000010000000200000003", "check wal segment name");
|
||||
|
||||
TEST_RESULT_STR_Z(jsonFromVar(varNewVarLst(dbList(db.primary))), "[[16384,\"test1\",13777]]", "check db list");
|
||||
TEST_RESULT_STR_Z(jsonFromVar(varNewVarLst(dbTablespaceList(db.primary))), "[]", "check tablespace list");
|
||||
|
||||
DbBackupStopResult backupStopResult = {.lsn = NULL};
|
||||
TEST_ASSIGN(backupStopResult, dbBackupStop(db.primary), "stop backup");
|
||||
TEST_RESULT_STR_Z(backupStopResult.lsn, "2/4", "check lsn");
|
||||
TEST_RESULT_STR_Z(backupStopResult.walSegmentName, "000000010000000200000004", "check wal segment name");
|
||||
TEST_RESULT_STR_Z(backupStopResult.backupLabel, NULL, "check backup label is not set");
|
||||
TEST_RESULT_STR_Z(backupStopResult.tablespaceMap, NULL, "check tablespace map is not set");
|
||||
|
||||
TEST_RESULT_VOID(dbFree(db.primary), "free primary");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("PostgreSQL 9.5 start/stop backup where backup is in progress");
|
||||
|
||||
harnessPqScriptSet((HarnessPq [])
|
||||
{
|
||||
// Connect to primary
|
||||
HRNPQ_MACRO_OPEN_GE_92(1, "dbname='postgres' port=5432", PG_VERSION_95, "/pg1", false, NULL, NULL),
|
||||
|
||||
// Start backup when backup is in progress
|
||||
HRNPQ_MACRO_ADVISORY_LOCK(1, true),
|
||||
HRNPQ_MACRO_IS_IN_BACKUP(1, true),
|
||||
|
||||
// Stop old backup
|
||||
HRNPQ_MACRO_STOP_BACKUP_LE_95(1, "1/1", "000000010000000100000001"),
|
||||
|
||||
// Start backup
|
||||
HRNPQ_MACRO_START_BACKUP_84_95(1, true, "2/5", "000000010000000200000005"),
|
||||
|
||||
// Stop backup
|
||||
HRNPQ_MACRO_STOP_BACKUP_LE_95(1, "2/6", "000000010000000200000006"),
|
||||
|
||||
// Close primary
|
||||
HRNPQ_MACRO_CLOSE(1),
|
||||
|
||||
HRNPQ_MACRO_DONE()
|
||||
});
|
||||
|
||||
TEST_ASSIGN(db, dbGet(true, true, false), "get primary");
|
||||
|
||||
TEST_RESULT_STR_Z(dbBackupStart(db.primary, true, true).lsn, "2/5", "start backup");
|
||||
|
||||
TEST_RESULT_LOG(
|
||||
"P00 WARN: the cluster is already in backup mode but no pgBackRest backup process is running."
|
||||
" pg_stop_backup() will be called so a new backup can be started.");
|
||||
|
||||
TEST_RESULT_STR_Z(dbBackupStop(db.primary).lsn, "2/6", "stop backup");
|
||||
|
||||
TEST_RESULT_VOID(dbFree(db.primary), "free primary");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("PostgreSQL 9.6 start/stop backup");
|
||||
|
||||
harnessPqScriptSet((HarnessPq [])
|
||||
{
|
||||
// Connect to primary
|
||||
HRNPQ_MACRO_OPEN_GE_92(1, "dbname='postgres' port=5432", PG_VERSION_96, "/pg1", false, NULL, NULL),
|
||||
|
||||
// Start backup
|
||||
HRNPQ_MACRO_ADVISORY_LOCK(1, true),
|
||||
HRNPQ_MACRO_START_BACKUP_96(1, false, "3/3", "000000010000000300000003"),
|
||||
|
||||
// Stop backup
|
||||
HRNPQ_MACRO_STOP_BACKUP_96(1, "3/4", "000000010000000300000004", false),
|
||||
|
||||
// Close primary
|
||||
HRNPQ_MACRO_CLOSE(1),
|
||||
|
||||
HRNPQ_MACRO_DONE()
|
||||
});
|
||||
|
||||
TEST_ASSIGN(db, dbGet(true, true, false), "get primary");
|
||||
|
||||
TEST_ASSIGN(backupStartResult, dbBackupStart(db.primary, false, false), "start backup");
|
||||
TEST_RESULT_STR_Z(backupStartResult.lsn, "3/3", "check lsn");
|
||||
TEST_RESULT_STR_Z(backupStartResult.walSegmentName, "000000010000000300000003", "check wal segment name");
|
||||
|
||||
TEST_ASSIGN(backupStopResult, dbBackupStop(db.primary), "stop backup");
|
||||
TEST_RESULT_STR_Z(backupStopResult.lsn, "3/4", "check lsn");
|
||||
TEST_RESULT_STR_Z(backupStopResult.walSegmentName, "000000010000000300000004", "check wal segment name");
|
||||
TEST_RESULT_STR_Z(backupStopResult.backupLabel, "BACKUP_LABEL_DATA", "check backup label");
|
||||
TEST_RESULT_STR_Z(backupStopResult.tablespaceMap, NULL, "check tablespace map is not set");
|
||||
|
||||
TEST_RESULT_VOID(dbFree(db.primary), "free primary");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("PostgreSQL 9.5 start backup from standby");
|
||||
|
||||
argList = strLstNew();
|
||||
strLstAddZ(argList, "--stanza=test1");
|
||||
strLstAddZ(argList, "--repo1-retention-full=1");
|
||||
strLstAddZ(argList, "--pg1-path=/pg1");
|
||||
strLstAddZ(argList, "--pg2-path=/pg2");
|
||||
strLstAddZ(argList, "--pg2-port=5433");
|
||||
harnessCfgLoad(cfgCmdBackup, argList);
|
||||
|
||||
harnessPqScriptSet((HarnessPq [])
|
||||
{
|
||||
// Connect to primary
|
||||
HRNPQ_MACRO_OPEN_GE_92(1, "dbname='postgres' port=5432", PG_VERSION_95, "/pg1", false, NULL, NULL),
|
||||
|
||||
// Connect to standby
|
||||
HRNPQ_MACRO_OPEN_GE_92(2, "dbname='postgres' port=5433", PG_VERSION_95, "/pg2", true, NULL, NULL),
|
||||
|
||||
// Start backup
|
||||
HRNPQ_MACRO_ADVISORY_LOCK(1, true),
|
||||
HRNPQ_MACRO_START_BACKUP_84_95(1, false, "5/4", "000000050000000500000004"),
|
||||
|
||||
// Wait for standby to sync
|
||||
HRNPQ_MACRO_REPLAY_WAIT_LE_95(2, "5/4"),
|
||||
|
||||
// Close standby
|
||||
HRNPQ_MACRO_CLOSE(2),
|
||||
|
||||
// Close primary
|
||||
HRNPQ_MACRO_CLOSE(1),
|
||||
|
||||
HRNPQ_MACRO_DONE()
|
||||
});
|
||||
|
||||
TEST_ASSIGN(db, dbGet(false, true, true), "get primary and standby");
|
||||
|
||||
TEST_RESULT_STR_Z(dbBackupStart(db.primary, false, false).lsn, "5/4", "start backup");
|
||||
TEST_RESULT_VOID(dbReplayWait(db.standby, STRDEF("5/4"), 1000), "sync standby");
|
||||
|
||||
TEST_RESULT_VOID(dbFree(db.standby), "free standby");
|
||||
TEST_RESULT_VOID(dbFree(db.primary), "free primary");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("PostgreSQL 10 start/stop backup from standby");
|
||||
|
||||
harnessPqScriptSet((HarnessPq [])
|
||||
{
|
||||
// Connect to primary
|
||||
HRNPQ_MACRO_OPEN_GE_92(1, "dbname='postgres' port=5432", PG_VERSION_10, "/pg1", false, NULL, NULL),
|
||||
|
||||
// Connect to standby
|
||||
HRNPQ_MACRO_OPEN_GE_92(2, "dbname='postgres' port=5433", PG_VERSION_10, "/pg2", true, NULL, NULL),
|
||||
|
||||
// Start backup
|
||||
HRNPQ_MACRO_ADVISORY_LOCK(1, true),
|
||||
HRNPQ_MACRO_START_BACKUP_GE_10(1, false, "5/5", "000000050000000500000005"),
|
||||
|
||||
// Standby returns NULL lsn
|
||||
{.session = 2,
|
||||
.function = HRNPQ_SENDQUERY,
|
||||
.param =
|
||||
"[\"select replayLsn::text,\\n"
|
||||
" (replayLsn > '5/5')::bool as targetReached\\n"
|
||||
" from pg_catalog.pg_last_wal_replay_lsn() as replayLsn\"]",
|
||||
.resultInt = 1},
|
||||
{.session = 2, .function = HRNPQ_CONSUMEINPUT},
|
||||
{.session = 2, .function = HRNPQ_ISBUSY},
|
||||
{.session = 2, .function = HRNPQ_GETRESULT},
|
||||
{.session = 2, .function = HRNPQ_RESULTSTATUS, .resultInt = PGRES_TUPLES_OK},
|
||||
{.session = 2, .function = HRNPQ_NTUPLES, .resultInt = 1},
|
||||
{.session = 2, .function = HRNPQ_NFIELDS, .resultInt = 2},
|
||||
{.session = 2, .function = HRNPQ_FTYPE, .param = "[0]", .resultInt = HRNPQ_TYPE_TEXT},
|
||||
{.session = 2, .function = HRNPQ_FTYPE, .param = "[1]", .resultInt = HRNPQ_TYPE_BOOL},
|
||||
{.session = 2, .function = HRNPQ_GETVALUE, .param = "[0,0]", .resultZ = ""},
|
||||
{.session = 2, .function = HRNPQ_GETISNULL, .param = "[0,0]", .resultInt = 1},
|
||||
{.session = 2, .function = HRNPQ_GETVALUE, .param = "[0,1]", .resultZ = "false"},
|
||||
{.session = 2, .function = HRNPQ_CLEAR},
|
||||
{.session = 2, .function = HRNPQ_GETRESULT, .resultNull = true},
|
||||
|
||||
// Timeout waiting for sync
|
||||
HRNPQ_MACRO_REPLAY_TARGET_REACHED_GE_10(2, "5/5", false, "5/3"),
|
||||
HRNPQ_MACRO_REPLAY_TARGET_REACHED_PROGRESS_GE_10(2, "5/5", false, "5/3", "5/3", false, 250),
|
||||
HRNPQ_MACRO_REPLAY_TARGET_REACHED_PROGRESS_GE_10(2, "5/5", false, "5/3", "5/3", false, 0),
|
||||
|
||||
// Checkpoint target not reached
|
||||
HRNPQ_MACRO_REPLAY_TARGET_REACHED_GE_10(2, "5/5", true, "5/5"),
|
||||
HRNPQ_MACRO_CHECKPOINT(2),
|
||||
HRNPQ_MACRO_CHECKPOINT_TARGET_REACHED_GE_10(2, "5/5", false, "5/4"),
|
||||
|
||||
// Wait for standby to sync
|
||||
HRNPQ_MACRO_REPLAY_TARGET_REACHED_GE_10(2, "5/5", false, "5/3"),
|
||||
HRNPQ_MACRO_REPLAY_TARGET_REACHED_PROGRESS_GE_10(2, "5/5", false, "5/3", "5/3", false, 0),
|
||||
HRNPQ_MACRO_REPLAY_TARGET_REACHED_PROGRESS_GE_10(2, "5/5", false, "5/4", "5/3", true, 0),
|
||||
HRNPQ_MACRO_REPLAY_TARGET_REACHED_PROGRESS_GE_10(2, "5/5", true, "5/5", "5/4", true, 0),
|
||||
HRNPQ_MACRO_CHECKPOINT(2),
|
||||
HRNPQ_MACRO_CHECKPOINT_TARGET_REACHED_GE_10(2, "5/5", true, "X/X"),
|
||||
|
||||
// Close standby
|
||||
HRNPQ_MACRO_CLOSE(2),
|
||||
|
||||
// Stop backup
|
||||
HRNPQ_MACRO_STOP_BACKUP_GE_10(1, "5/6", "000000050000000500000006", true),
|
||||
|
||||
// Close primary
|
||||
HRNPQ_MACRO_CLOSE(1),
|
||||
|
||||
HRNPQ_MACRO_DONE()
|
||||
});
|
||||
|
||||
TEST_ASSIGN(db, dbGet(false, true, true), "get primary and standby");
|
||||
|
||||
TEST_RESULT_STR_Z(dbBackupStart(db.primary, false, false).lsn, "5/5", "start backup");
|
||||
|
||||
TEST_ERROR(
|
||||
dbReplayWait(db.standby, STRDEF("5/5"), 1000), ArchiveTimeoutError,
|
||||
"unable to query replay lsn on the standby using 'pg_catalog.pg_last_wal_replay_lsn()'\n"
|
||||
"HINT: Is this a standby?");
|
||||
|
||||
TEST_ERROR(
|
||||
dbReplayWait(db.standby, STRDEF("5/5"), 200), ArchiveTimeoutError,
|
||||
"timeout before standby replayed to 5/5 - only reached 5/3");
|
||||
|
||||
TEST_ERROR(
|
||||
dbReplayWait(db.standby, STRDEF("5/5"), 1000), ArchiveTimeoutError,
|
||||
"the checkpoint lsn 5/4 is less than the target lsn 5/5 even though the replay lsn is 5/5");
|
||||
|
||||
TEST_RESULT_VOID(dbReplayWait(db.standby, STRDEF("5/5"), 1000), "sync standby");
|
||||
|
||||
TEST_RESULT_VOID(dbFree(db.standby), "free standby");
|
||||
|
||||
TEST_RESULT_STR_Z(dbBackupStop(db.primary).tablespaceMap, "TABLESPACE_MAP_DATA", "stop backup");
|
||||
|
||||
TEST_RESULT_VOID(dbFree(db.primary), "free primary");
|
||||
}
|
||||
|
||||
// *****************************************************************************************************************************
|
||||
if (testBegin("dbGet()"))
|
||||
{
|
||||
@ -160,7 +498,7 @@ testRun(void)
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
harnessPqScriptSet((HarnessPq [])
|
||||
{
|
||||
HRNPQ_MACRO_OPEN_84(1, "dbname='postgres' port=5432", "/pgdata", NULL, NULL),
|
||||
HRNPQ_MACRO_OPEN_LE_91(1, "dbname='postgres' port=5432", PG_VERSION_84, "/pgdata", NULL, NULL),
|
||||
HRNPQ_MACRO_CLOSE(1),
|
||||
HRNPQ_MACRO_DONE()
|
||||
});
|
||||
@ -188,8 +526,8 @@ testRun(void)
|
||||
|
||||
harnessPqScriptSet((HarnessPq [])
|
||||
{
|
||||
HRNPQ_MACRO_OPEN_84(1, "dbname='postgres' port=5432", "/pgdata", NULL, NULL),
|
||||
HRNPQ_MACRO_OPEN_84(8, "dbname='postgres' port=5433", "/pgdata", NULL, NULL),
|
||||
HRNPQ_MACRO_OPEN_LE_91(1, "dbname='postgres' port=5432", PG_VERSION_84, "/pgdata", NULL, NULL),
|
||||
HRNPQ_MACRO_OPEN_LE_91(8, "dbname='postgres' port=5433", PG_VERSION_84, "/pgdata", NULL, NULL),
|
||||
|
||||
HRNPQ_MACRO_CLOSE(1),
|
||||
HRNPQ_MACRO_CLOSE(8),
|
||||
|
Loading…
Reference in New Issue
Block a user