mirror of
https://github.com/pgbackrest/pgbackrest.git
synced 2025-01-18 04:58:51 +02:00
Add Db object to encapsulate PostgreSQL queries and commands.
Migrate functionality from the Perl Db module to C. For now this is just enough to implement the WAL switch check. Add the dbGet() helper function to get Db objects easily. Create macros in harnessPq to make writing pq scripts easier by grouping commonly used functions together. Reviewed by Cynthia Shang.
This commit is contained in:
parent
f9e1f3a798
commit
e4901d50d5
@ -70,6 +70,14 @@
|
||||
<p>Add Perl interface to C storage layer.</p>
|
||||
</release-item>
|
||||
|
||||
<release-item>
|
||||
<release-item-contributor-list>
|
||||
<release-item-reviewer id="cynthia.shang"/>
|
||||
</release-item-contributor-list>
|
||||
|
||||
<p>Add <code>Db</code> object to encapsulate <postgres/> queries and commands.</p>
|
||||
</release-item>
|
||||
|
||||
<release-item>
|
||||
<release-item-contributor-list>
|
||||
<release-item-reviewer id="cynthia.shang"/>
|
||||
|
@ -120,6 +120,9 @@ SRCS = \
|
||||
config/load.c \
|
||||
config/parse.c \
|
||||
config/protocol.c \
|
||||
db/db.c \
|
||||
db/helper.c \
|
||||
db/protocol.c \
|
||||
info/info.c \
|
||||
info/infoArchive.c \
|
||||
info/infoBackup.c \
|
||||
@ -249,7 +252,7 @@ command/info/info.o: command/info/info.c build.auto.h command/archive/common.h c
|
||||
command/local/local.o: command/local/local.c build.auto.h command/archive/get/protocol.h command/archive/push/protocol.h command/backup/protocol.h command/restore/protocol.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/handleRead.h common/io/handleWrite.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/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/helper.h protocol/server.h
|
||||
$(CC) $(CPPFLAGS) $(CFLAGS) $(CMAKE) -c command/local/local.c -o command/local/local.o
|
||||
|
||||
command/remote/remote.o: command/remote/remote.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/handleRead.h common/io/handleWrite.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/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/helper.h protocol/server.h storage/remote/protocol.h
|
||||
command/remote/remote.o: command/remote/remote.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/handleRead.h common/io/handleWrite.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/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 db/protocol.h protocol/client.h protocol/command.h protocol/helper.h protocol/server.h storage/remote/protocol.h
|
||||
$(CC) $(CPPFLAGS) $(CFLAGS) $(CMAKE) -c command/remote/remote.c -o command/remote/remote.o
|
||||
|
||||
command/restore/file.o: command/restore/file.c build.auto.h command/restore/file.h common/assert.h common/compress/gzip/common.h common/compress/gzip/decompress.h common/crypto/cipherBlock.h common/crypto/common.h common/crypto/hash.h common/debug.h common/error.auto.h common/error.h common/io/filter/filter.h common/io/filter/group.h common/io/filter/size.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/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 storage/helper.h storage/info.h storage/read.h storage/storage.h storage/write.h
|
||||
@ -426,6 +429,15 @@ 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/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/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 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/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 version.h
|
||||
$(CC) $(CPPFLAGS) $(CFLAGS) $(CMAKE) -c db/helper.c -o db/helper.o
|
||||
|
||||
db/protocol.o: db/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/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/protocol.h postgres/client.h postgres/interface.h protocol/client.h protocol/command.h protocol/server.h
|
||||
$(CC) $(CPPFLAGS) $(CFLAGS) $(CMAKE) -c db/protocol.c -o db/protocol.o
|
||||
|
||||
info/info.o: info/info.c build.auto.h common/assert.h common/crypto/cipherBlock.h common/crypto/common.h common/crypto/hash.h common/debug.h common/error.auto.h common/error.h common/ini.h common/io/filter/filter.h common/io/filter/filter.intern.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/json.h common/type/keyValue.h common/type/string.h common/type/stringList.h common/type/variant.h common/type/variantList.h info/info.h storage/helper.h storage/info.h storage/read.h storage/storage.h storage/write.h version.h
|
||||
$(CC) $(CPPFLAGS) $(CFLAGS) $(CMAKE) -c info/info.c -o info/info.o
|
||||
|
||||
|
@ -11,6 +11,7 @@ Remote Command
|
||||
#include "common/log.h"
|
||||
#include "config/config.h"
|
||||
#include "config/protocol.h"
|
||||
#include "db/protocol.h"
|
||||
#include "protocol/helper.h"
|
||||
#include "protocol/server.h"
|
||||
#include "storage/remote/protocol.h"
|
||||
@ -33,6 +34,7 @@ cmdRemote(int handleRead, int handleWrite)
|
||||
|
||||
ProtocolServer *server = protocolServerNew(name, PROTOCOL_SERVICE_REMOTE_STR, read, write);
|
||||
protocolServerHandlerAdd(server, storageRemoteProtocol);
|
||||
protocolServerHandlerAdd(server, dbProtocol);
|
||||
protocolServerHandlerAdd(server, configProtocol);
|
||||
|
||||
// Acquire a lock if this command needs one. We'll use the noop that is always sent from the client right after the
|
||||
|
313
src/db/db.c
Normal file
313
src/db/db.c
Normal file
@ -0,0 +1,313 @@
|
||||
/***********************************************************************************************************************************
|
||||
Database Client
|
||||
***********************************************************************************************************************************/
|
||||
#include "build.auto.h"
|
||||
|
||||
#include "common/debug.h"
|
||||
#include "common/log.h"
|
||||
#include "common/memContext.h"
|
||||
#include "common/object.h"
|
||||
#include "db/db.h"
|
||||
#include "db/protocol.h"
|
||||
#include "postgres/interface.h"
|
||||
#include "postgres/version.h"
|
||||
#include "version.h"
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Object type
|
||||
***********************************************************************************************************************************/
|
||||
struct Db
|
||||
{
|
||||
MemContext *memContext;
|
||||
PgClient *client; // Local PostgreSQL client
|
||||
ProtocolClient *remoteClient; // Protocol client for remote db queries
|
||||
unsigned int remoteIdx; // Index provided by the remote on open for subsequent calls
|
||||
const String *applicationName; // Used to identify this connection in PostgreSQL
|
||||
|
||||
unsigned int pgVersion; // Version as reported by the database
|
||||
const String *pgDataPath; // Data directory reported by the database
|
||||
};
|
||||
|
||||
OBJECT_DEFINE_FREE(DB);
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Close protocol connection. No need to close a locally created PgClient since it has its own destructor.
|
||||
***********************************************************************************************************************************/
|
||||
OBJECT_DEFINE_FREE_RESOURCE_BEGIN(DB, LOG, logLevelTrace)
|
||||
{
|
||||
ProtocolCommand *command = protocolCommandNew(PROTOCOL_COMMAND_DB_CLOSE_STR);
|
||||
protocolCommandParamAdd(command, VARUINT(this->remoteIdx));
|
||||
|
||||
protocolClientExecute(this->remoteClient, command, false);
|
||||
}
|
||||
OBJECT_DEFINE_FREE_RESOURCE_END(LOG);
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Create object
|
||||
***********************************************************************************************************************************/
|
||||
Db *
|
||||
dbNew(PgClient *client, ProtocolClient *remoteClient, const String *applicationName)
|
||||
{
|
||||
FUNCTION_LOG_BEGIN(logLevelDebug);
|
||||
FUNCTION_LOG_PARAM(PG_CLIENT, client);
|
||||
FUNCTION_LOG_PARAM(PROTOCOL_CLIENT, remoteClient);
|
||||
FUNCTION_LOG_PARAM(STRING, applicationName);
|
||||
FUNCTION_LOG_END();
|
||||
|
||||
ASSERT((client != NULL && remoteClient == NULL) || (client == NULL && remoteClient != NULL));
|
||||
ASSERT(applicationName != NULL);
|
||||
|
||||
Db *this = NULL;
|
||||
|
||||
MEM_CONTEXT_NEW_BEGIN("Db")
|
||||
{
|
||||
this = memNew(sizeof(Db));
|
||||
this->memContext = memContextCurrent();
|
||||
|
||||
this->client = pgClientMove(client, this->memContext);
|
||||
this->remoteClient = remoteClient;
|
||||
this->applicationName = strDup(applicationName);
|
||||
}
|
||||
MEM_CONTEXT_NEW_END();
|
||||
|
||||
FUNCTION_LOG_RETURN(DB, this);
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Execute a query
|
||||
***********************************************************************************************************************************/
|
||||
static VariantList *
|
||||
dbQuery(Db *this, const String *query)
|
||||
{
|
||||
FUNCTION_LOG_BEGIN(logLevelDebug);
|
||||
FUNCTION_LOG_PARAM(DB, this);
|
||||
FUNCTION_LOG_PARAM(STRING, query);
|
||||
FUNCTION_LOG_END();
|
||||
|
||||
ASSERT(this != NULL);
|
||||
ASSERT(query != NULL);
|
||||
|
||||
VariantList *result = NULL;
|
||||
|
||||
// Query remotely
|
||||
if (this->remoteClient != NULL)
|
||||
{
|
||||
ProtocolCommand *command = protocolCommandNew(PROTOCOL_COMMAND_DB_QUERY_STR);
|
||||
protocolCommandParamAdd(command, VARUINT(this->remoteIdx));
|
||||
protocolCommandParamAdd(command, VARSTR(query));
|
||||
|
||||
result = varVarLst(protocolClientExecute(this->remoteClient, command, true));
|
||||
}
|
||||
// Else locally
|
||||
else
|
||||
result = pgClientQuery(this->client, query);
|
||||
|
||||
FUNCTION_LOG_RETURN(VARIANT_LIST, result);
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Execute a command that expects no output
|
||||
***********************************************************************************************************************************/
|
||||
static void
|
||||
dbExec(Db *this, const String *command)
|
||||
{
|
||||
FUNCTION_LOG_BEGIN(logLevelDebug);
|
||||
FUNCTION_LOG_PARAM(DB, this);
|
||||
FUNCTION_LOG_PARAM(STRING, command);
|
||||
FUNCTION_LOG_END();
|
||||
|
||||
ASSERT(this != NULL);
|
||||
ASSERT(command != NULL);
|
||||
|
||||
CHECK(dbQuery(this, command) == NULL);
|
||||
|
||||
FUNCTION_LOG_RETURN_VOID();
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Execute a query that returns a single row and column
|
||||
***********************************************************************************************************************************/
|
||||
static Variant *
|
||||
dbQueryColumn(Db *this, const String *query)
|
||||
{
|
||||
FUNCTION_LOG_BEGIN(logLevelDebug);
|
||||
FUNCTION_LOG_PARAM(DB, this);
|
||||
FUNCTION_LOG_PARAM(STRING, query);
|
||||
FUNCTION_LOG_END();
|
||||
|
||||
ASSERT(this != NULL);
|
||||
ASSERT(query != NULL);
|
||||
|
||||
VariantList *result = dbQuery(this, query);
|
||||
|
||||
CHECK(varLstSize(result) == 1);
|
||||
CHECK(varLstSize(varVarLst(varLstGet(result, 0))) == 1);
|
||||
|
||||
FUNCTION_LOG_RETURN(VARIANT, varLstGet(varVarLst(varLstGet(result, 0)), 0));
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Execute a query that returns a single row
|
||||
***********************************************************************************************************************************/
|
||||
static VariantList *
|
||||
dbQueryRow(Db *this, const String *query)
|
||||
{
|
||||
FUNCTION_LOG_BEGIN(logLevelDebug);
|
||||
FUNCTION_LOG_PARAM(DB, this);
|
||||
FUNCTION_LOG_PARAM(STRING, query);
|
||||
FUNCTION_LOG_END();
|
||||
|
||||
ASSERT(this != NULL);
|
||||
ASSERT(query != NULL);
|
||||
|
||||
VariantList *result = dbQuery(this, query);
|
||||
|
||||
CHECK(varLstSize(result) == 1);
|
||||
|
||||
FUNCTION_LOG_RETURN(VARIANT_LIST, varVarLst(varLstGet(result, 0)));
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Open the db connection
|
||||
***********************************************************************************************************************************/
|
||||
void
|
||||
dbOpen(Db *this)
|
||||
{
|
||||
FUNCTION_LOG_BEGIN(logLevelDebug);
|
||||
FUNCTION_LOG_PARAM(DB, this);
|
||||
FUNCTION_LOG_END();
|
||||
|
||||
ASSERT(this != NULL);
|
||||
|
||||
MEM_CONTEXT_TEMP_BEGIN()
|
||||
{
|
||||
// Open the connection
|
||||
if (this->remoteClient != NULL)
|
||||
{
|
||||
ProtocolCommand *command = protocolCommandNew(PROTOCOL_COMMAND_DB_OPEN_STR);
|
||||
this->remoteIdx = varUIntForce(protocolClientExecute(this->remoteClient, command, true));
|
||||
|
||||
// Set a callback to notify the remote when a connection is closed
|
||||
memContextCallbackSet(this->memContext, dbFreeResource, this);
|
||||
}
|
||||
else
|
||||
pgClientOpen(this->client);
|
||||
|
||||
// Set search_path to prevent overrides of the functions we expect to call. All queries should also be schema-qualified,
|
||||
// but this is an extra level protection.
|
||||
dbExec(this, STRDEF("set search_path = 'pg_catalog'"));
|
||||
|
||||
// Query the version and data_directory
|
||||
VariantList *row = dbQueryRow(
|
||||
this,
|
||||
STRDEF(
|
||||
"select (select setting from pg_catalog.pg_settings where name = 'server_version_num')::int4,"
|
||||
" (select setting from pg_catalog.pg_settings where name = 'data_directory')::text"));
|
||||
|
||||
// Strip the minor version off since we don't need it. In the future it might be a good idea to warn users when they are
|
||||
// running an old minor version.
|
||||
this->pgVersion = varUIntForce(varLstGet(row, 0)) / 100 * 100;
|
||||
|
||||
// Store the data directory that PostgreSQL is running in. This can be compared to the configured pgBackRest directory when
|
||||
// validating the configuration.
|
||||
MEM_CONTEXT_BEGIN(this->memContext)
|
||||
{
|
||||
this->pgDataPath = strDup(varStr(varLstGet(row, 1)));
|
||||
}
|
||||
MEM_CONTEXT_END();
|
||||
|
||||
if (this->pgVersion >= PG_VERSION_APPLICATION_NAME)
|
||||
dbExec(this, strNewFmt("set application_name = '%s'", strPtr(this->applicationName)));
|
||||
}
|
||||
MEM_CONTEXT_TEMP_END();
|
||||
|
||||
FUNCTION_LOG_RETURN_VOID();
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Is this instance a standby?
|
||||
***********************************************************************************************************************************/
|
||||
bool
|
||||
dbIsStandby(Db *this)
|
||||
{
|
||||
FUNCTION_LOG_BEGIN(logLevelDebug);
|
||||
FUNCTION_LOG_PARAM(DB, this);
|
||||
FUNCTION_LOG_END();
|
||||
|
||||
ASSERT(this != NULL);
|
||||
|
||||
bool result = false;
|
||||
|
||||
if (this->pgVersion >= PG_VERSION_HOT_STANDBY)
|
||||
{
|
||||
result = varBool(dbQueryColumn(this, STRDEF("select pg_catalog.pg_is_in_recovery()")));
|
||||
}
|
||||
|
||||
FUNCTION_LOG_RETURN(BOOL, result);
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Switch the WAL segment and return the segment that should have been archived
|
||||
***********************************************************************************************************************************/
|
||||
String *
|
||||
dbWalSwitch(Db *this)
|
||||
{
|
||||
FUNCTION_LOG_BEGIN(logLevelDebug);
|
||||
FUNCTION_LOG_PARAM(DB, this);
|
||||
FUNCTION_LOG_END();
|
||||
|
||||
ASSERT(this != NULL);
|
||||
|
||||
String *result = NULL;
|
||||
|
||||
MEM_CONTEXT_TEMP_BEGIN()
|
||||
{
|
||||
// Create a restore point to ensure current WAL will be archived. For versions < 9.1 activity will need to be generated by
|
||||
// the user if there have been no writes since the last WAL switch.
|
||||
if (this->pgVersion >= PG_VERSION_RESTORE_POINT)
|
||||
dbQueryColumn(this, STRDEF("select pg_catalog.pg_create_restore_point('" PROJECT_NAME " Archive Check')::text"));
|
||||
|
||||
// Request a WAL segment switch
|
||||
const char *walName = strPtr(pgWalName(this->pgVersion));
|
||||
const String *walFileName = varStr(
|
||||
dbQueryColumn(this, strNewFmt("select pg_catalog.pg_%sfile_name(pg_catalog.pg_switch_%s())::text", walName, walName)));
|
||||
|
||||
// Copy WAL segment name to the calling context
|
||||
memContextSwitch(MEM_CONTEXT_OLD());
|
||||
result = strDup(walFileName);
|
||||
memContextSwitch(MEM_CONTEXT_TEMP());
|
||||
}
|
||||
MEM_CONTEXT_TEMP_END();
|
||||
|
||||
FUNCTION_LOG_RETURN(STRING, result);
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Move the object to a new context
|
||||
***********************************************************************************************************************************/
|
||||
Db *
|
||||
dbMove(Db *this, MemContext *parentNew)
|
||||
{
|
||||
FUNCTION_TEST_BEGIN();
|
||||
FUNCTION_TEST_PARAM(DB, this);
|
||||
FUNCTION_TEST_PARAM(MEM_CONTEXT, parentNew);
|
||||
FUNCTION_TEST_END();
|
||||
|
||||
ASSERT(parentNew != NULL);
|
||||
|
||||
if (this != NULL)
|
||||
memContextMove(this->memContext, parentNew);
|
||||
|
||||
FUNCTION_TEST_RETURN(this);
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Render as string for logging
|
||||
***********************************************************************************************************************************/
|
||||
String *
|
||||
dbToLog(const Db *this)
|
||||
{
|
||||
return strNewFmt(
|
||||
"{client: %s, remoteClient: %s}", this->client == NULL ? "null" : strPtr(pgClientToLog(this->client)),
|
||||
this->remoteClient == NULL ? "null" : strPtr(protocolClientToLog(this->remoteClient)));
|
||||
}
|
51
src/db/db.h
Normal file
51
src/db/db.h
Normal file
@ -0,0 +1,51 @@
|
||||
/***********************************************************************************************************************************
|
||||
Database Client
|
||||
|
||||
Implements the required PostgreSQL queries and commands. Notice that there is no general purpose query function -- all queries are
|
||||
expected to be embedded in this object.
|
||||
***********************************************************************************************************************************/
|
||||
#ifndef DB_DB_H
|
||||
#define DB_DB_H
|
||||
|
||||
#include "postgres/client.h"
|
||||
#include "protocol/client.h"
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Object type
|
||||
***********************************************************************************************************************************/
|
||||
#define DB_TYPE Db
|
||||
#define DB_PREFIX db
|
||||
|
||||
typedef struct Db Db;
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Constructor
|
||||
***********************************************************************************************************************************/
|
||||
Db *dbNew(PgClient *client, ProtocolClient *remoteClient, const String *applicationName);
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Functions
|
||||
***********************************************************************************************************************************/
|
||||
void dbOpen(Db *this);
|
||||
bool dbIsStandby(Db *this);
|
||||
String *dbWalSwitch(Db *this);
|
||||
void dbClose(Db *this);
|
||||
|
||||
Db *dbMove(Db *this, MemContext *parentNew);
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Destructor
|
||||
***********************************************************************************************************************************/
|
||||
void dbFree(Db *this);
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Macros for function logging
|
||||
***********************************************************************************************************************************/
|
||||
String *dbToLog(const Db *this);
|
||||
|
||||
#define FUNCTION_LOG_DB_TYPE \
|
||||
Db *
|
||||
#define FUNCTION_LOG_DB_FORMAT(value, buffer, bufferSize) \
|
||||
FUNCTION_LOG_STRING_OBJECT_FORMAT(value, dbToLog, buffer, bufferSize)
|
||||
|
||||
#endif
|
127
src/db/helper.c
Normal file
127
src/db/helper.c
Normal file
@ -0,0 +1,127 @@
|
||||
/***********************************************************************************************************************************
|
||||
Database Helper
|
||||
***********************************************************************************************************************************/
|
||||
#include "build.auto.h"
|
||||
|
||||
#include "common/debug.h"
|
||||
#include "config/config.h"
|
||||
#include "db/helper.h"
|
||||
#include "postgres/interface.h"
|
||||
#include "protocol/helper.h"
|
||||
#include "version.h"
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Get specified cluster
|
||||
***********************************************************************************************************************************/
|
||||
static Db *
|
||||
dbGetId(unsigned int pgId)
|
||||
{
|
||||
FUNCTION_LOG_BEGIN(logLevelDebug);
|
||||
FUNCTION_LOG_PARAM(UINT, pgId);
|
||||
FUNCTION_LOG_END();
|
||||
|
||||
ASSERT(pgId > 0);
|
||||
|
||||
Db *result = NULL;
|
||||
|
||||
MEM_CONTEXT_TEMP_BEGIN()
|
||||
{
|
||||
const String *applicationName = strNewFmt(PROJECT_NAME " [%s]", cfgCommandName(cfgCommand()));
|
||||
|
||||
if (pgIsLocal(pgId))
|
||||
{
|
||||
result = dbNew(
|
||||
pgClientNew(
|
||||
cfgOptionStr(cfgOptPgSocketPath + pgId - 1), cfgOptionUInt(cfgOptPgPort + pgId - 1), PG_DB_POSTGRES_STR, NULL,
|
||||
(TimeMSec)(cfgOptionDbl(cfgOptDbTimeout) * MSEC_PER_SEC)),
|
||||
NULL, applicationName);
|
||||
}
|
||||
else
|
||||
result = dbNew(NULL, protocolRemoteGet(protocolStorageTypePg, pgId), applicationName);
|
||||
|
||||
dbMove(result, MEM_CONTEXT_OLD());
|
||||
}
|
||||
MEM_CONTEXT_TEMP_END();
|
||||
|
||||
FUNCTION_LOG_RETURN(DB, result);
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Get primary cluster or primary and standby cluster
|
||||
***********************************************************************************************************************************/
|
||||
DbGetResult
|
||||
dbGet(bool primaryOnly, bool primaryRequired)
|
||||
{
|
||||
FUNCTION_LOG_BEGIN(logLevelDebug);
|
||||
FUNCTION_LOG_PARAM(BOOL, primaryOnly);
|
||||
FUNCTION_LOG_PARAM(BOOL, primaryRequired);
|
||||
FUNCTION_LOG_END();
|
||||
|
||||
DbGetResult result = {0};
|
||||
|
||||
MEM_CONTEXT_TEMP_BEGIN()
|
||||
{
|
||||
// Loop through to look for primary and standby (if required)
|
||||
for (unsigned int pgIdx = 0; pgIdx < cfgOptionIndexTotal(cfgOptPgPath); pgIdx++)
|
||||
{
|
||||
if (cfgOptionTest(cfgOptPgHost + pgIdx) || cfgOptionTest(cfgOptPgPath + pgIdx))
|
||||
{
|
||||
Db *db = NULL;
|
||||
bool standby = false;
|
||||
|
||||
TRY_BEGIN()
|
||||
{
|
||||
db = dbGetId(pgIdx + 1);
|
||||
dbOpen(db);
|
||||
standby = dbIsStandby(db);
|
||||
}
|
||||
CATCH_ANY()
|
||||
{
|
||||
dbFree(db);
|
||||
db = NULL;
|
||||
|
||||
LOG_WARN("unable to check pg-%u: [%s] %s", pgIdx + 1, errorTypeName(errorType()), errorMessage());
|
||||
}
|
||||
TRY_END();
|
||||
|
||||
// Was the connection successful
|
||||
if (db != NULL)
|
||||
{
|
||||
// Is this cluster a standby
|
||||
if (standby)
|
||||
{
|
||||
// If a standby has not already been found then assign it
|
||||
if (result.standbyId == 0 && !primaryOnly)
|
||||
{
|
||||
result.standbyId = pgIdx + 1;
|
||||
result.standby = db;
|
||||
}
|
||||
// Else close the connection since we don't need it
|
||||
else
|
||||
dbFree(db);
|
||||
}
|
||||
// Else is a primary
|
||||
else
|
||||
{
|
||||
// Error if more than one primary was found
|
||||
if (result.primaryId != 0)
|
||||
THROW(DbConnectError, "more than one primary cluster found");
|
||||
|
||||
result.primaryId = pgIdx + 1;
|
||||
result.primary = db;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Error if no primary was found
|
||||
if (result.primaryId == 0 && primaryRequired)
|
||||
THROW(DbConnectError, "unable to find primary cluster - cannot proceed");
|
||||
|
||||
dbMove(result.primary, MEM_CONTEXT_OLD());
|
||||
dbMove(result.standby, MEM_CONTEXT_OLD());
|
||||
}
|
||||
MEM_CONTEXT_TEMP_END();
|
||||
|
||||
FUNCTION_LOG_RETURN(DB_GET_RESULT, result);
|
||||
}
|
34
src/db/helper.h
Normal file
34
src/db/helper.h
Normal file
@ -0,0 +1,34 @@
|
||||
/***********************************************************************************************************************************
|
||||
Database Helper
|
||||
|
||||
Helper functions for getting connections to PostgreSQL.
|
||||
***********************************************************************************************************************************/
|
||||
#ifndef DB_HELPER_H
|
||||
#define DB_HELPER_H
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "db/db.h"
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Functions
|
||||
***********************************************************************************************************************************/
|
||||
typedef struct DbGetResult
|
||||
{
|
||||
unsigned int primaryId;
|
||||
Db *primary;
|
||||
unsigned int standbyId;
|
||||
Db *standby;
|
||||
} DbGetResult;
|
||||
|
||||
DbGetResult dbGet(bool primaryOnly, bool primaryRequired);
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Macros for function logging
|
||||
***********************************************************************************************************************************/
|
||||
#define FUNCTION_LOG_DB_GET_RESULT_TYPE \
|
||||
DbGetResult
|
||||
#define FUNCTION_LOG_DB_GET_RESULT_FORMAT(value, buffer, bufferSize) \
|
||||
objToLog(&value, "DbGetResult", buffer, bufferSize)
|
||||
|
||||
#endif
|
96
src/db/protocol.c
Normal file
96
src/db/protocol.c
Normal file
@ -0,0 +1,96 @@
|
||||
/***********************************************************************************************************************************
|
||||
Db Protocol Handler
|
||||
***********************************************************************************************************************************/
|
||||
#include "build.auto.h"
|
||||
|
||||
#include "common/debug.h"
|
||||
#include "common/io/io.h"
|
||||
#include "common/log.h"
|
||||
#include "common/memContext.h"
|
||||
#include "common/type/list.h"
|
||||
#include "config/config.h"
|
||||
#include "db/protocol.h"
|
||||
#include "postgres/client.h"
|
||||
#include "postgres/interface.h"
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Constants
|
||||
***********************************************************************************************************************************/
|
||||
STRING_EXTERN(PROTOCOL_COMMAND_DB_OPEN_STR, PROTOCOL_COMMAND_DB_OPEN);
|
||||
STRING_EXTERN(PROTOCOL_COMMAND_DB_QUERY_STR, PROTOCOL_COMMAND_DB_QUERY);
|
||||
STRING_EXTERN(PROTOCOL_COMMAND_DB_CLOSE_STR, PROTOCOL_COMMAND_DB_CLOSE);
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Local variables
|
||||
***********************************************************************************************************************************/
|
||||
static struct
|
||||
{
|
||||
List *pgClientList; // List of db objects
|
||||
} dbProtocolLocal;
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Process db protocol requests
|
||||
***********************************************************************************************************************************/
|
||||
bool
|
||||
dbProtocol(const String *command, const VariantList *paramList, ProtocolServer *server)
|
||||
{
|
||||
FUNCTION_LOG_BEGIN(logLevelDebug);
|
||||
FUNCTION_LOG_PARAM(STRING, command);
|
||||
FUNCTION_LOG_PARAM(VARIANT_LIST, paramList);
|
||||
FUNCTION_LOG_PARAM(PROTOCOL_SERVER, server);
|
||||
FUNCTION_LOG_END();
|
||||
|
||||
ASSERT(command != NULL);
|
||||
|
||||
// Attempt to satisfy the request -- we may get requests that are meant for other handlers
|
||||
bool found = true;
|
||||
|
||||
MEM_CONTEXT_TEMP_BEGIN()
|
||||
{
|
||||
if (strEq(command, PROTOCOL_COMMAND_DB_OPEN_STR))
|
||||
{
|
||||
// If the db list does not exist then create it in the calling context (which should be persistent)
|
||||
if (dbProtocolLocal.pgClientList == NULL)
|
||||
{
|
||||
memContextSwitch(MEM_CONTEXT_OLD());
|
||||
dbProtocolLocal.pgClientList = lstNew(sizeof(PgClient *));
|
||||
memContextSwitch(MEM_CONTEXT_TEMP());
|
||||
}
|
||||
|
||||
// Add db to the list
|
||||
unsigned int dbIdx = lstSize(dbProtocolLocal.pgClientList);
|
||||
|
||||
MEM_CONTEXT_BEGIN(lstMemContext(dbProtocolLocal.pgClientList))
|
||||
{
|
||||
// Only a single db is passed to the remote
|
||||
PgClient *pgClient = pgClientNew(
|
||||
cfgOptionStr(cfgOptPgSocketPath), cfgOptionUInt(cfgOptPgPort), PG_DB_POSTGRES_STR, NULL,
|
||||
(TimeMSec)(cfgOptionDbl(cfgOptDbTimeout) * MSEC_PER_SEC));
|
||||
pgClientOpen(pgClient);
|
||||
|
||||
lstAdd(dbProtocolLocal.pgClientList, &pgClient);
|
||||
}
|
||||
MEM_CONTEXT_END();
|
||||
|
||||
// Return db index which should be included in subsequent calls
|
||||
protocolServerResponse(server, VARUINT(dbIdx));
|
||||
}
|
||||
else if (strEq(command, PROTOCOL_COMMAND_DB_QUERY_STR) || strEq(command, PROTOCOL_COMMAND_DB_CLOSE_STR))
|
||||
{
|
||||
PgClient *pgClient = *(PgClient **)lstGet(dbProtocolLocal.pgClientList, varUIntForce(varLstGet(paramList, 0)));
|
||||
|
||||
if (strEq(command, PROTOCOL_COMMAND_DB_QUERY_STR))
|
||||
protocolServerResponse(server, varNewVarLst(pgClientQuery(pgClient, varStr(varLstGet(paramList, 1)))));
|
||||
else
|
||||
{
|
||||
pgClientClose(pgClient);
|
||||
protocolServerResponse(server, NULL);
|
||||
}
|
||||
}
|
||||
else
|
||||
found = false;
|
||||
}
|
||||
MEM_CONTEXT_TEMP_END();
|
||||
|
||||
FUNCTION_LOG_RETURN(BOOL, found);
|
||||
}
|
27
src/db/protocol.h
Normal file
27
src/db/protocol.h
Normal file
@ -0,0 +1,27 @@
|
||||
/***********************************************************************************************************************************
|
||||
Db Protocol Handler
|
||||
***********************************************************************************************************************************/
|
||||
#ifndef DB_PROTOCOL_H
|
||||
#define DB_PROTOCOL_H
|
||||
|
||||
#include "common/type/string.h"
|
||||
#include "common/type/variantList.h"
|
||||
#include "protocol/client.h"
|
||||
#include "protocol/server.h"
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Constants
|
||||
***********************************************************************************************************************************/
|
||||
#define PROTOCOL_COMMAND_DB_OPEN "dbOpen"
|
||||
STRING_DECLARE(PROTOCOL_COMMAND_DB_OPEN_STR);
|
||||
#define PROTOCOL_COMMAND_DB_QUERY "dbQuery"
|
||||
STRING_DECLARE(PROTOCOL_COMMAND_DB_QUERY_STR);
|
||||
#define PROTOCOL_COMMAND_DB_CLOSE "dbClose"
|
||||
STRING_DECLARE(PROTOCOL_COMMAND_DB_CLOSE_STR);
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Functions
|
||||
***********************************************************************************************************************************/
|
||||
bool dbProtocol(const String *command, const VariantList *paramList, ProtocolServer *server);
|
||||
|
||||
#endif
|
@ -9,10 +9,7 @@ Postgres Client
|
||||
#include "common/log.h"
|
||||
#include "common/memContext.h"
|
||||
#include "common/object.h"
|
||||
#include "common/time.h"
|
||||
#include "common/type/list.h"
|
||||
#include "common/type/string.h"
|
||||
#include "common/type/variantList.h"
|
||||
#include "common/wait.h"
|
||||
#include "postgres/client.h"
|
||||
|
||||
@ -350,15 +347,36 @@ pgClientClose(PgClient *this)
|
||||
FUNCTION_LOG_END();
|
||||
|
||||
ASSERT(this != NULL);
|
||||
CHECK(this->connection != NULL);
|
||||
|
||||
memContextCallbackClear(this->memContext);
|
||||
PQfinish(this->connection);
|
||||
this->connection = NULL;
|
||||
if (this->connection != NULL)
|
||||
{
|
||||
memContextCallbackClear(this->memContext);
|
||||
PQfinish(this->connection);
|
||||
this->connection = NULL;
|
||||
}
|
||||
|
||||
FUNCTION_LOG_RETURN_VOID();
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Move the pg client object to a new context
|
||||
***********************************************************************************************************************************/
|
||||
PgClient *
|
||||
pgClientMove(PgClient *this, MemContext *parentNew)
|
||||
{
|
||||
FUNCTION_TEST_BEGIN();
|
||||
FUNCTION_TEST_PARAM(PG_CLIENT, this);
|
||||
FUNCTION_TEST_PARAM(MEM_CONTEXT, parentNew);
|
||||
FUNCTION_TEST_END();
|
||||
|
||||
ASSERT(parentNew != NULL);
|
||||
|
||||
if (this != NULL)
|
||||
memContextMove(this->memContext, parentNew);
|
||||
|
||||
FUNCTION_TEST_RETURN(this);
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Render as string for logging
|
||||
***********************************************************************************************************************************/
|
||||
|
@ -8,6 +8,10 @@ casts to queries to output one of these types.
|
||||
#ifndef POSTGRES_QUERY_H
|
||||
#define POSTGRES_QUERY_H
|
||||
|
||||
#include "common/type/string.h"
|
||||
#include "common/type/variantList.h"
|
||||
#include "common/time.h"
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Object type
|
||||
***********************************************************************************************************************************/
|
||||
@ -29,6 +33,8 @@ PgClient *pgClientOpen(PgClient *this);
|
||||
VariantList *pgClientQuery(PgClient *this, const String *query);
|
||||
void pgClientClose(PgClient *this);
|
||||
|
||||
PgClient *pgClientMove(PgClient *this, MemContext *parentNew);
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Destructor
|
||||
***********************************************************************************************************************************/
|
||||
|
@ -14,6 +14,12 @@ PostgreSQL Interface
|
||||
#include "postgres/version.h"
|
||||
#include "storage/helper.h"
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Defines for various Postgres paths and files
|
||||
***********************************************************************************************************************************/
|
||||
STRING_EXTERN(PG_NAME_WAL_STR, PG_NAME_WAL);
|
||||
STRING_EXTERN(PG_NAME_XLOG_STR, PG_NAME_XLOG);
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Define default wal segment size
|
||||
|
||||
@ -35,6 +41,11 @@ something far larger needed but <= the minimum read size on just about any syste
|
||||
***********************************************************************************************************************************/
|
||||
#define PG_WAL_HEADER_SIZE ((unsigned int)(512))
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Name of default PostgreSQL database used for running all queries and commands
|
||||
***********************************************************************************************************************************/
|
||||
STRING_EXTERN(PG_DB_POSTGRES_STR, PG_DB_POSTGRES);
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
PostgreSQL interface definitions
|
||||
|
||||
@ -407,6 +418,19 @@ pgWalFromFile(const String *walFile)
|
||||
FUNCTION_LOG_RETURN(PG_WAL, result);
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Get WAL name (wal/xlog) for a PostgreSQL version
|
||||
***********************************************************************************************************************************/
|
||||
const String *
|
||||
pgWalName(unsigned int pgVersion)
|
||||
{
|
||||
FUNCTION_TEST_BEGIN();
|
||||
FUNCTION_TEST_PARAM(UINT, pgVersion);
|
||||
FUNCTION_TEST_END();
|
||||
|
||||
FUNCTION_TEST_RETURN(pgVersion >= PG_VERSION_WAL_RENAME ? PG_NAME_WAL_STR : PG_NAME_XLOG_STR);
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Create pg_control for testing
|
||||
***********************************************************************************************************************************/
|
||||
|
@ -17,6 +17,17 @@ Defines for various Postgres paths and files
|
||||
#define PG_PATH_ARCHIVE_STATUS "archive_status"
|
||||
#define PG_PATH_GLOBAL "global"
|
||||
|
||||
#define PG_NAME_WAL "wal"
|
||||
STRING_DECLARE(PG_NAME_WAL_STR);
|
||||
#define PG_NAME_XLOG "xlog"
|
||||
STRING_DECLARE(PG_NAME_XLOG_STR);
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Name of default PostgreSQL database used for running all queries and commands
|
||||
***********************************************************************************************************************************/
|
||||
#define PG_DB_POSTGRES "postgres"
|
||||
STRING_DECLARE(PG_DB_POSTGRES_STR);
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Define default page size
|
||||
|
||||
@ -69,6 +80,8 @@ String *pgVersionToStr(unsigned int version);
|
||||
PgWal pgWalFromFile(const String *walFile);
|
||||
PgWal pgWalFromBuffer(const Buffer *walBuffer);
|
||||
|
||||
const String *pgWalName(unsigned int pgVersion);
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Test Functions
|
||||
***********************************************************************************************************************************/
|
||||
|
@ -26,6 +26,21 @@ PostgreSQL version constants
|
||||
|
||||
#define PG_VERSION_MAX PG_VERSION_11
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Version where various PostgreSQL capabilities were introduced
|
||||
***********************************************************************************************************************************/
|
||||
// application_name can be set to show the application name in pg_stat_activity
|
||||
#define PG_VERSION_APPLICATION_NAME PG_VERSION_90
|
||||
|
||||
// pg_is_in_recovery() supported
|
||||
#define PG_VERSION_HOT_STANDBY PG_VERSION_91
|
||||
|
||||
// pg_create_restore_point() supported
|
||||
#define PG_VERSION_RESTORE_POINT PG_VERSION_91
|
||||
|
||||
// xlog was renamed to wal
|
||||
#define PG_VERSION_WAL_RENAME PG_VERSION_10
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
PostgreSQL version string constants for use in error messages
|
||||
***********************************************************************************************************************************/
|
||||
|
@ -326,7 +326,7 @@ unit:
|
||||
|
||||
# ----------------------------------------------------------------------------------------------------------------------------
|
||||
- name: interface
|
||||
total: 5
|
||||
total: 6
|
||||
|
||||
coverage:
|
||||
postgres/interface: full
|
||||
@ -539,6 +539,20 @@ unit:
|
||||
- name: info-backup-perl
|
||||
total: 3
|
||||
|
||||
# ********************************************************************************************************************************
|
||||
- name: db
|
||||
|
||||
test:
|
||||
# ----------------------------------------------------------------------------------------------------------------------------
|
||||
- name: db
|
||||
total: 2
|
||||
perlReq: true
|
||||
|
||||
coverage:
|
||||
db/db: full
|
||||
db/helper: full
|
||||
db/protocol: full
|
||||
|
||||
# ********************************************************************************************************************************
|
||||
- name: command
|
||||
|
||||
|
@ -64,7 +64,8 @@ harnessPqScriptRun(const char *function, const VariantList *param, HarnessPq *pa
|
||||
harnessPqScriptFail = true;
|
||||
|
||||
THROW_FMT(
|
||||
AssertError, "pq script [%u] expected function '%s' but got '%s'", harnessPqScriptIdx, result->function, function);
|
||||
AssertError, "pq script [%u] expected function %s (%s) but got %s (%s)", harnessPqScriptIdx, result->function,
|
||||
result->param == NULL ? "" : result->param, function, strPtr(paramStr));
|
||||
}
|
||||
|
||||
// Check that parameters match
|
||||
|
@ -9,7 +9,11 @@ usage examples.
|
||||
|
||||
#ifndef HARNESS_PQ_REAL
|
||||
|
||||
#include <libpq-fe.h>
|
||||
|
||||
#include "common/macro.h"
|
||||
#include "common/time.h"
|
||||
#include "version.h"
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Function constants
|
||||
@ -34,6 +38,115 @@ Function constants
|
||||
#define HRNPQ_SENDQUERY "PQsendQuery"
|
||||
#define HRNPQ_STATUS "PQstatus"
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Macros for defining groups of functions that implement various queries and commands
|
||||
***********************************************************************************************************************************/
|
||||
#define HRNPQ_MACRO_OPEN(sessionParam, connectParam) \
|
||||
{.session = sessionParam, .function = HRNPQ_CONNECTDB, .param = "[\"" connectParam "\"]"}, \
|
||||
{.session = sessionParam, .function = HRNPQ_STATUS, .resultInt = CONNECTION_OK}
|
||||
|
||||
#define HRNPQ_MACRO_SET_SEARCH_PATH(sessionParam) \
|
||||
{.session = sessionParam, .function = HRNPQ_SENDQUERY, .param = "[\"set search_path = 'pg_catalog'\"]", .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_VALIDATE_QUERY(sessionParam, versionParam, pgPathParam) \
|
||||
{.session = sessionParam, .function = HRNPQ_SENDQUERY, .param = \
|
||||
"[\"select (select setting from pg_catalog.pg_settings where name = 'server_version_num')::int4," \
|
||||
" (select setting from pg_catalog.pg_settings where name = 'data_directory')::text\"]", \
|
||||
.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 = STRINGIFY(versionParam)}, \
|
||||
{.session = sessionParam, .function = HRNPQ_GETVALUE, .param = "[0,1]", .resultZ = pgPathParam}, \
|
||||
{.session = sessionParam, .function = HRNPQ_CLEAR}, \
|
||||
{.session = sessionParam, .function = HRNPQ_GETRESULT, .resultNull = true}
|
||||
|
||||
#define HRNPQ_MACRO_SET_APPLICATION_NAME(sessionParam) \
|
||||
{.session = sessionParam, .function = HRNPQ_SENDQUERY, \
|
||||
.param = strPtr(strNewFmt("[\"set application_name = '" PROJECT_NAME " [%s]'\"]", cfgCommandName(cfgCommand()))), \
|
||||
.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_IS_STANDBY_QUERY(sessionParam, standbyParam) \
|
||||
{.session = sessionParam, .function = HRNPQ_SENDQUERY, .param = "[\"select pg_catalog.pg_is_in_recovery()\"]", .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 = STRINGIFY(standbyParam)}, \
|
||||
{.session = sessionParam, .function = HRNPQ_CLEAR}, \
|
||||
{.session = sessionParam, .function = HRNPQ_GETRESULT, .resultNull = true}
|
||||
|
||||
#define HRNPQ_MACRO_CREATE_RESTORE_POINT(sessionParam, lsnParam) \
|
||||
{.session = sessionParam, \
|
||||
.function = HRNPQ_SENDQUERY, .param = "[\"select pg_catalog.pg_create_restore_point('pgBackRest Archive Check')::text\"]", \
|
||||
.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_TEXT}, \
|
||||
{.session = sessionParam, .function = HRNPQ_GETVALUE, .param = "[0,0]", .resultZ = lsnParam}, \
|
||||
{.session = sessionParam, .function = HRNPQ_CLEAR}, \
|
||||
{.session = sessionParam, .function = HRNPQ_GETRESULT, .resultNull = true}
|
||||
|
||||
#define HRNPQ_MACRO_WAL_SWITCH(sessionParam, walNameParam, walFileNameParam) \
|
||||
{.session = sessionParam, .function = HRNPQ_SENDQUERY, \
|
||||
.param = "[\"select pg_catalog.pg_" walNameParam "file_name(pg_catalog.pg_switch_" walNameParam "())::text\"]", \
|
||||
.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_TEXT}, \
|
||||
{.session = sessionParam, .function = HRNPQ_GETVALUE, .param = "[0,0]", .resultZ = walFileNameParam}, \
|
||||
{.session = sessionParam, .function = HRNPQ_CLEAR}, \
|
||||
{.session = sessionParam, .function = HRNPQ_GETRESULT, .resultNull = true}
|
||||
|
||||
#define HRNPQ_MACRO_CLOSE(sessionParam) \
|
||||
{.session = sessionParam, .function = HRNPQ_FINISH}
|
||||
|
||||
#define HRNPQ_MACRO_DONE() \
|
||||
{.function = NULL}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Macros to simplify dbOpen() for specific database versions
|
||||
***********************************************************************************************************************************/
|
||||
#define HRNPQ_MACRO_OPEN_84(sessionParam, connectParam, pgPathParam) \
|
||||
HRNPQ_MACRO_OPEN(sessionParam, connectParam), \
|
||||
HRNPQ_MACRO_SET_SEARCH_PATH(sessionParam), \
|
||||
HRNPQ_MACRO_VALIDATE_QUERY(sessionParam, PG_VERSION_84, pgPathParam)
|
||||
|
||||
#define HRNPQ_MACRO_OPEN_92(sessionParam, connectParam, pgPathParam, standbyParam) \
|
||||
HRNPQ_MACRO_OPEN(sessionParam, connectParam), \
|
||||
HRNPQ_MACRO_SET_SEARCH_PATH(sessionParam), \
|
||||
HRNPQ_MACRO_VALIDATE_QUERY(sessionParam, PG_VERSION_92, pgPathParam), \
|
||||
HRNPQ_MACRO_SET_APPLICATION_NAME(sessionParam), \
|
||||
HRNPQ_MACRO_IS_STANDBY_QUERY(sessionParam, standbyParam)
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Data type constants
|
||||
***********************************************************************************************************************************/
|
||||
|
279
test/src/module/db/dbTest.c
Normal file
279
test/src/module/db/dbTest.c
Normal file
@ -0,0 +1,279 @@
|
||||
/***********************************************************************************************************************************
|
||||
Test Database
|
||||
***********************************************************************************************************************************/
|
||||
#include "common/harnessConfig.h"
|
||||
#include "common/harnessFork.h"
|
||||
#include "common/harnessLog.h"
|
||||
#include "common/harnessPq.h"
|
||||
|
||||
#include "common/io/handleRead.h"
|
||||
#include "common/io/handleWrite.h"
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Test Run
|
||||
***********************************************************************************************************************************/
|
||||
void
|
||||
testRun(void)
|
||||
{
|
||||
FUNCTION_HARNESS_VOID();
|
||||
|
||||
// *****************************************************************************************************************************
|
||||
if (testBegin("Db and dbProtocol()"))
|
||||
{
|
||||
HARNESS_FORK_BEGIN()
|
||||
{
|
||||
HARNESS_FORK_CHILD_BEGIN(0, true)
|
||||
{
|
||||
IoRead *read = ioHandleReadNew(strNew("client read"), HARNESS_FORK_CHILD_READ(), 2000);
|
||||
ioReadOpen(read);
|
||||
IoWrite *write = ioHandleWriteNew(strNew("client write"), HARNESS_FORK_CHILD_WRITE());
|
||||
ioWriteOpen(write);
|
||||
|
||||
// Set options
|
||||
StringList *argList = strLstNew();
|
||||
strLstAddZ(argList, "pgbackrest");
|
||||
strLstAddZ(argList, "--stanza=test1");
|
||||
strLstAddZ(argList, "--pg1-path=/path/to/pg");
|
||||
strLstAddZ(argList, "--command=backup");
|
||||
strLstAddZ(argList, "--type=db");
|
||||
strLstAddZ(argList, "--process=0");
|
||||
strLstAddZ(argList, "remote");
|
||||
harnessCfgLoad(strLstSize(argList), strLstPtr(argList));
|
||||
|
||||
// Set script
|
||||
harnessPqScriptSet((HarnessPq [])
|
||||
{
|
||||
HRNPQ_MACRO_OPEN(1, "dbname='postgres' port=5432"),
|
||||
HRNPQ_MACRO_SET_SEARCH_PATH(1),
|
||||
HRNPQ_MACRO_VALIDATE_QUERY(1, PG_VERSION_84, "/pgdata"),
|
||||
HRNPQ_MACRO_CLOSE(1),
|
||||
|
||||
HRNPQ_MACRO_OPEN(1, "dbname='postgres' port=5432"),
|
||||
HRNPQ_MACRO_SET_SEARCH_PATH(1),
|
||||
HRNPQ_MACRO_VALIDATE_QUERY(1, PG_VERSION_84, "/pgdata"),
|
||||
HRNPQ_MACRO_WAL_SWITCH(1, "xlog", "000000030000000200000003"),
|
||||
HRNPQ_MACRO_CLOSE(1),
|
||||
|
||||
HRNPQ_MACRO_DONE()
|
||||
});
|
||||
|
||||
// Create server
|
||||
ProtocolServer *server = NULL;
|
||||
|
||||
TEST_ASSIGN(server, protocolServerNew(strNew("db test server"), strNew("test"), read, write), "create server");
|
||||
TEST_RESULT_VOID(protocolServerHandlerAdd(server, dbProtocol), "add handler");
|
||||
TEST_RESULT_VOID(protocolServerProcess(server), "run process loop");
|
||||
TEST_RESULT_VOID(protocolServerFree(server), "free server");
|
||||
}
|
||||
HARNESS_FORK_CHILD_END();
|
||||
|
||||
HARNESS_FORK_PARENT_BEGIN()
|
||||
{
|
||||
IoRead *read = ioHandleReadNew(strNew("server read"), HARNESS_FORK_PARENT_READ_PROCESS(0), 2000);
|
||||
ioReadOpen(read);
|
||||
IoWrite *write = ioHandleWriteNew(strNew("server write"), HARNESS_FORK_PARENT_WRITE_PROCESS(0));
|
||||
ioWriteOpen(write);
|
||||
|
||||
// Create client
|
||||
ProtocolClient *client = NULL;
|
||||
Db *db = NULL;
|
||||
|
||||
TEST_ASSIGN(client, protocolClientNew(strNew("db test client"), strNew("test"), read, write), "create client");
|
||||
|
||||
// Open and free database
|
||||
TEST_ASSIGN(db, dbNew(NULL, client, strNew("test")), "create db");
|
||||
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
|
||||
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");
|
||||
TEST_RESULT_VOID(memContextCallbackClear(db->memContext), "clear context so close is not called");
|
||||
|
||||
TEST_RESULT_VOID(protocolClientFree(client), "free client");
|
||||
}
|
||||
HARNESS_FORK_PARENT_END();
|
||||
}
|
||||
HARNESS_FORK_END();
|
||||
}
|
||||
|
||||
// *****************************************************************************************************************************
|
||||
if (testBegin("dbGet()"))
|
||||
{
|
||||
DbGetResult result = {0};
|
||||
|
||||
// Error connecting to primary
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
StringList *argList = strLstNew();
|
||||
strLstAddZ(argList, "pgbackrest");
|
||||
strLstAddZ(argList, "--stanza=test1");
|
||||
strLstAddZ(argList, "--repo1-retention-full=1");
|
||||
strLstAddZ(argList, "--pg1-path=/path/to/pg");
|
||||
strLstAddZ(argList, "backup");
|
||||
harnessCfgLoad(strLstSize(argList), strLstPtr(argList));
|
||||
|
||||
harnessPqScriptSet((HarnessPq [])
|
||||
{
|
||||
{.function = HRNPQ_CONNECTDB, .param = "[\"dbname='postgres' port=5432\"]"},
|
||||
{.function = HRNPQ_STATUS, .resultInt = CONNECTION_BAD},
|
||||
{.function = HRNPQ_ERRORMESSAGE, .resultZ = "error"},
|
||||
{.function = HRNPQ_FINISH},
|
||||
{.function = NULL}
|
||||
});
|
||||
|
||||
TEST_ERROR(dbGet(true, true), DbConnectError, "unable to find primary cluster - cannot proceed");
|
||||
harnessLogResult(
|
||||
"P00 WARN: unable to check pg-1: [DbConnectError] unable to connect to 'dbname='postgres' port=5432': error");
|
||||
|
||||
// Only cluster is a standby
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
harnessPqScriptSet((HarnessPq [])
|
||||
{
|
||||
HRNPQ_MACRO_OPEN(1, "dbname='postgres' port=5432"),
|
||||
HRNPQ_MACRO_SET_SEARCH_PATH(1),
|
||||
HRNPQ_MACRO_VALIDATE_QUERY(1, PG_VERSION_94, "/pgdata"),
|
||||
HRNPQ_MACRO_SET_APPLICATION_NAME(1),
|
||||
HRNPQ_MACRO_IS_STANDBY_QUERY(1, true),
|
||||
HRNPQ_MACRO_CLOSE(1),
|
||||
HRNPQ_MACRO_DONE()
|
||||
});
|
||||
|
||||
TEST_ERROR(dbGet(true, true), DbConnectError, "unable to find primary cluster - cannot proceed");
|
||||
|
||||
// Primary cluster found
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
harnessPqScriptSet((HarnessPq [])
|
||||
{
|
||||
HRNPQ_MACRO_OPEN_84(1, "dbname='postgres' port=5432", "/pgdata"),
|
||||
HRNPQ_MACRO_CLOSE(1),
|
||||
HRNPQ_MACRO_DONE()
|
||||
});
|
||||
|
||||
TEST_ASSIGN(result, dbGet(true, true), "get primary only");
|
||||
|
||||
TEST_RESULT_INT(result.primaryId, 1, " check primary id");
|
||||
TEST_RESULT_BOOL(result.primary != NULL, true, " check primary");
|
||||
TEST_RESULT_INT(result.standbyId, 0, " check standby id");
|
||||
TEST_RESULT_BOOL(result.standby == NULL, true, " check standby");
|
||||
|
||||
TEST_RESULT_VOID(dbFree(result.primary), "free primary");
|
||||
|
||||
// More than one primary found
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
argList = strLstNew();
|
||||
strLstAddZ(argList, "pgbackrest");
|
||||
strLstAddZ(argList, "--stanza=test1");
|
||||
strLstAddZ(argList, "--repo1-retention-full=1");
|
||||
strLstAddZ(argList, "--pg1-path=/path/to/pg1");
|
||||
strLstAddZ(argList, "--pg8-path=/path/to/pg2");
|
||||
strLstAddZ(argList, "--pg8-port=5433");
|
||||
strLstAddZ(argList, "backup");
|
||||
harnessCfgLoad(strLstSize(argList), strLstPtr(argList));
|
||||
|
||||
harnessPqScriptSet((HarnessPq [])
|
||||
{
|
||||
HRNPQ_MACRO_OPEN_84(1, "dbname='postgres' port=5432", "/pgdata"),
|
||||
HRNPQ_MACRO_OPEN_84(8, "dbname='postgres' port=5433", "/pgdata"),
|
||||
|
||||
HRNPQ_MACRO_CLOSE(1),
|
||||
HRNPQ_MACRO_CLOSE(8),
|
||||
|
||||
HRNPQ_MACRO_DONE()
|
||||
});
|
||||
|
||||
TEST_ERROR(dbGet(true, true), DbConnectError, "more than one primary cluster found");
|
||||
|
||||
// Two standbys found but no primary
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
harnessPqScriptSet((HarnessPq [])
|
||||
{
|
||||
HRNPQ_MACRO_OPEN_92(1, "dbname='postgres' port=5432", "/pgdata", true),
|
||||
HRNPQ_MACRO_OPEN_92(8, "dbname='postgres' port=5433", "/pgdata", true),
|
||||
|
||||
HRNPQ_MACRO_CLOSE(8),
|
||||
HRNPQ_MACRO_CLOSE(1),
|
||||
|
||||
HRNPQ_MACRO_DONE()
|
||||
});
|
||||
|
||||
TEST_ERROR(dbGet(false, true), DbConnectError, "unable to find primary cluster - cannot proceed");
|
||||
|
||||
// Two standbys and primary not required
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
harnessPqScriptSet((HarnessPq [])
|
||||
{
|
||||
HRNPQ_MACRO_OPEN_92(1, "dbname='postgres' port=5432", "/pgdata", true),
|
||||
HRNPQ_MACRO_OPEN_92(8, "dbname='postgres' port=5433", "/pgdata", true),
|
||||
|
||||
HRNPQ_MACRO_CLOSE(8),
|
||||
HRNPQ_MACRO_CLOSE(1),
|
||||
|
||||
HRNPQ_MACRO_DONE()
|
||||
});
|
||||
|
||||
TEST_ASSIGN(result, dbGet(false, false), "get standbys");
|
||||
|
||||
TEST_RESULT_INT(result.primaryId, 0, " check primary id");
|
||||
TEST_RESULT_BOOL(result.primary == NULL, true, " check primary");
|
||||
TEST_RESULT_INT(result.standbyId, 1, " check standby id");
|
||||
TEST_RESULT_BOOL(result.standby != NULL, true, " check standby");
|
||||
|
||||
TEST_RESULT_VOID(dbFree(result.standby), "free standby");
|
||||
|
||||
// Primary and standby found
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
argList = strLstNew();
|
||||
strLstAddZ(argList, "pgbackrest");
|
||||
strLstAddZ(argList, "--stanza=test1");
|
||||
strLstAddZ(argList, "--repo1-retention-full=1");
|
||||
strLstAddZ(argList, "--pg1-path=/path/to/pg1");
|
||||
strLstAddZ(argList, "--pg4-path=/path/to/pg4");
|
||||
strLstAddZ(argList, "--pg4-port=5433");
|
||||
strLstAddZ(argList, "--pg5-host=localhost");
|
||||
strLstAdd(argList, strNewFmt("--pg5-host-user=%s", testUser()));
|
||||
strLstAddZ(argList, "--pg5-path=/path/to/pg5");
|
||||
strLstAddZ(argList, "--pg8-path=/path/to/pg8");
|
||||
strLstAddZ(argList, "--pg8-port=5434");
|
||||
strLstAddZ(argList, "backup");
|
||||
harnessCfgLoad(strLstSize(argList), strLstPtr(argList));
|
||||
|
||||
harnessPqScriptSet((HarnessPq [])
|
||||
{
|
||||
HRNPQ_MACRO_OPEN_92(1, "dbname='postgres' port=5432", "/pgdata", true),
|
||||
|
||||
// pg-4 error
|
||||
{.session = 4, .function = HRNPQ_CONNECTDB, .param = "[\"dbname='postgres' port=5433\"]"},
|
||||
{.session = 4, .function = HRNPQ_STATUS, .resultInt = CONNECTION_BAD},
|
||||
{.session = 4, .function = HRNPQ_ERRORMESSAGE, .resultZ = "error"},
|
||||
{.session = 4, .function = HRNPQ_FINISH},
|
||||
|
||||
HRNPQ_MACRO_OPEN_92(8, "dbname='postgres' port=5434", "/pgdata", false),
|
||||
|
||||
HRNPQ_MACRO_CREATE_RESTORE_POINT(8, "2/3"),
|
||||
HRNPQ_MACRO_WAL_SWITCH(8, "xlog", "000000010000000200000003"),
|
||||
|
||||
HRNPQ_MACRO_CLOSE(8),
|
||||
HRNPQ_MACRO_CLOSE(1),
|
||||
|
||||
HRNPQ_MACRO_DONE()
|
||||
});
|
||||
|
||||
TEST_ASSIGN(result, dbGet(false, true), "get primary and standy");
|
||||
harnessLogResultRegExp(
|
||||
"P00 WARN: unable to check pg-4: \\[DbConnectError\\] unable to connect to 'dbname='postgres' port=5433': error\n"
|
||||
"P00 WARN: unable to check pg-5: \\[DbConnectError\\] raised from remote-0 protocol on 'localhost':"
|
||||
" unable to connect to 'dbname='postgres' port=5432': could not connect to server: No such file or directory.*");
|
||||
|
||||
TEST_RESULT_INT(result.primaryId, 8, " check primary id");
|
||||
TEST_RESULT_BOOL(result.primary != NULL, true, " check primary");
|
||||
TEST_RESULT_STR(strPtr(dbWalSwitch(result.primary)), "000000010000000200000003", " wal switch");
|
||||
TEST_RESULT_INT(result.standbyId, 1, " check standby id");
|
||||
TEST_RESULT_BOOL(result.standby != NULL, true, " check standby");
|
||||
|
||||
TEST_RESULT_VOID(dbFree(result.primary), "free primary");
|
||||
TEST_RESULT_VOID(dbFree(result.standby), "free standby");
|
||||
}
|
||||
|
||||
FUNCTION_HARNESS_RESULT_VOID();
|
||||
}
|
@ -53,7 +53,15 @@ testRun(void)
|
||||
#endif
|
||||
|
||||
PgClient *client = NULL;
|
||||
TEST_ASSIGN(client, pgClientNew(NULL, 5433, strNew("postg '\\res"), NULL, 3000), "new client");
|
||||
|
||||
MEM_CONTEXT_TEMP_BEGIN()
|
||||
{
|
||||
TEST_ASSIGN(client, pgClientNew(NULL, 5433, strNew("postg '\\res"), NULL, 3000), "new client");
|
||||
TEST_RESULT_VOID(pgClientMove(client, MEM_CONTEXT_OLD()), "move client");
|
||||
TEST_RESULT_VOID(pgClientMove(NULL, MEM_CONTEXT_OLD()), "move null client");
|
||||
}
|
||||
MEM_CONTEXT_TEMP_END();
|
||||
|
||||
TEST_ERROR(
|
||||
pgClientOpen(client), DbConnectError,
|
||||
"unable to connect to 'dbname='postg \\'\\\\res' port=5433': could not connect to server: No such file or directory\n"
|
||||
@ -284,6 +292,7 @@ testRun(void)
|
||||
});
|
||||
#endif
|
||||
TEST_RESULT_VOID(pgClientClose(client), "close client");
|
||||
TEST_RESULT_VOID(pgClientClose(client), "close client again");
|
||||
}
|
||||
|
||||
FUNCTION_HARNESS_RESULT_VOID();
|
||||
|
@ -94,6 +94,13 @@ testRun(void)
|
||||
TEST_RESULT_INT(info.version, PG_VERSION_83, " check version");
|
||||
}
|
||||
|
||||
// *****************************************************************************************************************************
|
||||
if (testBegin("pgWalName()"))
|
||||
{
|
||||
TEST_RESULT_STR(strPtr(pgWalName(PG_VERSION_96)), "xlog", "check xlog name");
|
||||
TEST_RESULT_STR(strPtr(pgWalName(PG_VERSION_10)), "wal", "check wal name");
|
||||
}
|
||||
|
||||
// *****************************************************************************************************************************
|
||||
if (testBegin("pgWalFromBuffer() and pgWalFromFile()"))
|
||||
{
|
||||
@ -157,7 +164,6 @@ testRun(void)
|
||||
"{version: 110000, systemId: 1030522662895, walSegmentSize: 16777216, pageChecksum: true}", "check log");
|
||||
}
|
||||
|
||||
|
||||
// *****************************************************************************************************************************
|
||||
if (testBegin("pgWalToLog()"))
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user