mirror of
https://github.com/pgbackrest/pgbackrest.git
synced 2025-06-06 23:16:26 +02:00
Rename FUNCTION_DEBUG_* macros to FUNCTION_LOG_* to more accurately reflect what they do. Further rename FUNCTION_DEBUG_RESULT* macros to FUNCTION_LOG_RETURN* to make it clearer that they return from the function as well as logging. Leave FUNCTION_TEST_* macros as they are. Consolidate the various ASSERT* macros into a single ASSERT macro that is always compiled out of production builds. It was difficult to figure out when an assert would be checked with all the different types in play. When ASSERTs are compiled in they will always be checked regardless of the log level -- tying these two concepts together was not a good idea.
366 lines
13 KiB
C
366 lines
13 KiB
C
/***********************************************************************************************************************************
|
|
PostgreSQL Interface
|
|
***********************************************************************************************************************************/
|
|
#include <string.h>
|
|
|
|
#include "common/debug.h"
|
|
#include "common/log.h"
|
|
#include "common/memContext.h"
|
|
#include "common/regExp.h"
|
|
#include "postgres/interface.h"
|
|
#include "postgres/interface/v083.h"
|
|
#include "postgres/interface/v084.h"
|
|
#include "postgres/interface/v090.h"
|
|
#include "postgres/interface/v091.h"
|
|
#include "postgres/interface/v092.h"
|
|
#include "postgres/interface/v093.h"
|
|
#include "postgres/interface/v094.h"
|
|
#include "postgres/interface/v095.h"
|
|
#include "postgres/interface/v096.h"
|
|
#include "postgres/interface/v100.h"
|
|
#include "postgres/interface/v110.h"
|
|
#include "postgres/version.h"
|
|
#include "storage/helper.h"
|
|
|
|
/***********************************************************************************************************************************
|
|
Define default page size
|
|
|
|
Page size can only be changed at compile time and is not known to be well-tested, so only the default page size is supported.
|
|
***********************************************************************************************************************************/
|
|
#define PG_PAGE_SIZE_DEFAULT ((unsigned int)(8 * 1024))
|
|
|
|
/***********************************************************************************************************************************
|
|
Define default wal segment size
|
|
|
|
Page size can only be changed at compile time and and is not known to be well-tested, so only the default page size is supported.
|
|
***********************************************************************************************************************************/
|
|
#define PG_WAL_SEGMENT_SIZE_DEFAULT ((unsigned int)(16 * 1024 * 1024))
|
|
|
|
/***********************************************************************************************************************************
|
|
Control file size. The control file is actually 8192 bytes but only the first 512 bytes are used to prevent torn pages even on
|
|
really old storage with 512-byte sectors. This is true across all versions of PostgreSQL.
|
|
***********************************************************************************************************************************/
|
|
#define PG_CONTROL_SIZE ((unsigned int)(8 * 1024))
|
|
#define PG_CONTROL_DATA_SIZE ((unsigned int)(512))
|
|
|
|
/***********************************************************************************************************************************
|
|
PostgreSQL interface definitions
|
|
***********************************************************************************************************************************/
|
|
typedef struct PgInterface
|
|
{
|
|
unsigned int version;
|
|
PgControl (*control)(const Buffer *);
|
|
bool (*is)(const Buffer *);
|
|
void (*controlTest)(PgControl, Buffer *);
|
|
} PgInterface;
|
|
|
|
static const PgInterface pgInterface[] =
|
|
{
|
|
{
|
|
.version = PG_VERSION_11,
|
|
.control = pgInterfaceControl110,
|
|
.is = pgInterfaceIs110,
|
|
|
|
#ifdef DEBUG
|
|
.controlTest = pgInterfaceControlTest110,
|
|
#endif
|
|
},
|
|
{
|
|
.version = PG_VERSION_10,
|
|
.control = pgInterfaceControl100,
|
|
.is = pgInterfaceIs100,
|
|
|
|
#ifdef DEBUG
|
|
.controlTest = pgInterfaceControlTest100,
|
|
#endif
|
|
},
|
|
{
|
|
.version = PG_VERSION_96,
|
|
.control = pgInterfaceControl096,
|
|
.is = pgInterfaceIs096,
|
|
|
|
#ifdef DEBUG
|
|
.controlTest = pgInterfaceControlTest096,
|
|
#endif
|
|
},
|
|
{
|
|
.version = PG_VERSION_95,
|
|
.control = pgInterfaceControl095,
|
|
.is = pgInterfaceIs095,
|
|
|
|
#ifdef DEBUG
|
|
.controlTest = pgInterfaceControlTest095,
|
|
#endif
|
|
},
|
|
{
|
|
.version = PG_VERSION_94,
|
|
.control = pgInterfaceControl094,
|
|
.is = pgInterfaceIs094,
|
|
|
|
#ifdef DEBUG
|
|
.controlTest = pgInterfaceControlTest094,
|
|
#endif
|
|
},
|
|
{
|
|
.version = PG_VERSION_93,
|
|
.control = pgInterfaceControl093,
|
|
.is = pgInterfaceIs093,
|
|
|
|
#ifdef DEBUG
|
|
.controlTest = pgInterfaceControlTest093,
|
|
#endif
|
|
},
|
|
{
|
|
.version = PG_VERSION_92,
|
|
.control = pgInterfaceControl092,
|
|
.is = pgInterfaceIs092,
|
|
|
|
#ifdef DEBUG
|
|
.controlTest = pgInterfaceControlTest092,
|
|
#endif
|
|
},
|
|
{
|
|
.version = PG_VERSION_91,
|
|
.control = pgInterfaceControl091,
|
|
.is = pgInterfaceIs091,
|
|
|
|
#ifdef DEBUG
|
|
.controlTest = pgInterfaceControlTest091,
|
|
#endif
|
|
},
|
|
{
|
|
.version = PG_VERSION_90,
|
|
.control = pgInterfaceControl090,
|
|
.is = pgInterfaceIs090,
|
|
|
|
#ifdef DEBUG
|
|
.controlTest = pgInterfaceControlTest090,
|
|
#endif
|
|
},
|
|
{
|
|
.version = PG_VERSION_84,
|
|
.control = pgInterfaceControl084,
|
|
.is = pgInterfaceIs084,
|
|
|
|
#ifdef DEBUG
|
|
.controlTest = pgInterfaceControlTest084,
|
|
#endif
|
|
},
|
|
{
|
|
.version = PG_VERSION_83,
|
|
.control = pgInterfaceControl083,
|
|
.is = pgInterfaceIs083,
|
|
|
|
#ifdef DEBUG
|
|
.controlTest = pgInterfaceControlTest083,
|
|
#endif
|
|
},
|
|
};
|
|
|
|
/***********************************************************************************************************************************
|
|
These pg_control fields are common to all versions of PostgreSQL, so we can use them to generate error messages when the pg_control
|
|
version cannot be found.
|
|
***********************************************************************************************************************************/
|
|
typedef struct PgControlCommon
|
|
{
|
|
uint64_t systemId;
|
|
uint32_t controlVersion;
|
|
uint32_t catalogVersion;
|
|
} PgControlCommon;
|
|
|
|
/***********************************************************************************************************************************
|
|
Get info from pg_control
|
|
***********************************************************************************************************************************/
|
|
PgControl
|
|
pgControlFromBuffer(const Buffer *controlFile)
|
|
{
|
|
FUNCTION_LOG_BEGIN(logLevelTrace);
|
|
FUNCTION_LOG_PARAM(BUFFER, controlFile);
|
|
FUNCTION_LOG_END();
|
|
|
|
ASSERT(controlFile != NULL);
|
|
|
|
// Search for the version of PostgreSQL that uses this control file
|
|
const PgInterface *interface = NULL;
|
|
|
|
for (unsigned int interfaceIdx = 0; interfaceIdx < sizeof(pgInterface) / sizeof(PgInterface); interfaceIdx++)
|
|
{
|
|
if (pgInterface[interfaceIdx].is(controlFile))
|
|
{
|
|
interface = &pgInterface[interfaceIdx];
|
|
break;
|
|
}
|
|
}
|
|
|
|
// If the version was not found then error with the control and catalog version that were found
|
|
if (interface == NULL)
|
|
{
|
|
PgControlCommon *controlCommon = (PgControlCommon *)bufPtr(controlFile);
|
|
|
|
THROW_FMT(
|
|
VersionNotSupportedError,
|
|
"unexpected control version = %u and catalog version = %u\n"
|
|
"HINT: is this version of PostgreSQL supported?",
|
|
controlCommon->controlVersion, controlCommon->catalogVersion);
|
|
}
|
|
|
|
// Get info from the control file
|
|
PgControl result = interface->control(controlFile);
|
|
result.version = interface->version;
|
|
|
|
// Check the segment size
|
|
if (result.version < PG_VERSION_11 && result.walSegmentSize != PG_WAL_SEGMENT_SIZE_DEFAULT)
|
|
{
|
|
THROW_FMT(
|
|
FormatError, "wal segment size is %u but must be %u for " PG_NAME " <= " PG_VERSION_10_STR, result.walSegmentSize,
|
|
PG_WAL_SEGMENT_SIZE_DEFAULT);
|
|
}
|
|
|
|
// Check the page size
|
|
if (result.pageSize != PG_PAGE_SIZE_DEFAULT)
|
|
THROW_FMT(FormatError, "page size is %u but must be %u", result.pageSize, PG_PAGE_SIZE_DEFAULT);
|
|
|
|
FUNCTION_LOG_RETURN(PG_CONTROL, result);
|
|
}
|
|
|
|
/***********************************************************************************************************************************
|
|
Get info from pg_control
|
|
***********************************************************************************************************************************/
|
|
PgControl
|
|
pgControlFromFile(const String *pgPath)
|
|
{
|
|
FUNCTION_LOG_BEGIN(logLevelDebug);
|
|
FUNCTION_LOG_PARAM(STRING, pgPath);
|
|
FUNCTION_LOG_END();
|
|
|
|
ASSERT(pgPath != NULL);
|
|
|
|
PgControl result = {0};
|
|
|
|
MEM_CONTEXT_TEMP_BEGIN()
|
|
{
|
|
// Read control file
|
|
Buffer *controlFile = storageGetP(
|
|
storageNewReadNP(storageLocal(), strNewFmt("%s/" PG_PATH_GLOBAL "/" PG_FILE_PGCONTROL, strPtr(pgPath))),
|
|
.exactSize = PG_CONTROL_DATA_SIZE);
|
|
|
|
result = pgControlFromBuffer(controlFile);
|
|
}
|
|
MEM_CONTEXT_TEMP_END();
|
|
|
|
FUNCTION_LOG_RETURN(PG_CONTROL, result);
|
|
}
|
|
|
|
/***********************************************************************************************************************************
|
|
Create pg_control for testing
|
|
***********************************************************************************************************************************/
|
|
#ifdef DEBUG
|
|
|
|
Buffer *
|
|
pgControlTestToBuffer(PgControl pgControl)
|
|
{
|
|
FUNCTION_TEST_BEGIN();
|
|
FUNCTION_TEST_PARAM(PG_CONTROL, pgControl);
|
|
FUNCTION_TEST_END();
|
|
|
|
// Set defaults if values are not passed
|
|
pgControl.pageSize = pgControl.pageSize == 0 ? PG_PAGE_SIZE_DEFAULT : pgControl.pageSize;
|
|
pgControl.walSegmentSize = pgControl.walSegmentSize == 0 ? PG_WAL_SEGMENT_SIZE_DEFAULT : pgControl.walSegmentSize;
|
|
|
|
// Create the buffer and clear it
|
|
Buffer *result = bufNew(PG_CONTROL_SIZE);
|
|
memset(bufPtr(result), 0, bufSize(result));
|
|
bufUsedSet(result, bufSize(result));
|
|
|
|
// Find the interface for the version of PostgreSQL
|
|
const PgInterface *interface = NULL;
|
|
|
|
for (unsigned int interfaceIdx = 0; interfaceIdx < sizeof(pgInterface) / sizeof(PgInterface); interfaceIdx++)
|
|
{
|
|
if (pgInterface[interfaceIdx].version == pgControl.version)
|
|
{
|
|
interface = &pgInterface[interfaceIdx];
|
|
break;
|
|
}
|
|
}
|
|
|
|
// If the version was not found then error
|
|
if (interface == NULL)
|
|
THROW_FMT(AssertError, "invalid version %u", pgControl.version);
|
|
|
|
// Generate pg_control
|
|
interface->controlTest(pgControl, result);
|
|
|
|
FUNCTION_TEST_RETURN(BUFFER, result);
|
|
}
|
|
|
|
#endif
|
|
|
|
/***********************************************************************************************************************************
|
|
Convert version string to version number
|
|
***********************************************************************************************************************************/
|
|
unsigned int
|
|
pgVersionFromStr(const String *version)
|
|
{
|
|
FUNCTION_LOG_BEGIN(logLevelTrace);
|
|
FUNCTION_LOG_PARAM(STRING, version);
|
|
FUNCTION_LOG_END();
|
|
|
|
ASSERT(version != NULL);
|
|
|
|
unsigned int result = 0;
|
|
|
|
MEM_CONTEXT_TEMP_BEGIN()
|
|
{
|
|
// If format is not number.number (9.4) or number only (10) then error
|
|
if (!regExpMatchOne(STRING_CONST("^[0-9]+[.]*[0-9]+$"), version))
|
|
THROW_FMT(AssertError, "version %s format is invalid", strPtr(version));
|
|
|
|
// If there is a dot set the major and minor versions, else just the major
|
|
int idxStart = strChr(version, '.');
|
|
unsigned int major;
|
|
unsigned int minor = 0;
|
|
|
|
if (idxStart != -1)
|
|
{
|
|
major = cvtZToUInt(strPtr(strSubN(version, 0, (size_t)idxStart)));
|
|
minor = cvtZToUInt(strPtr(strSub(version, (size_t)idxStart + 1)));
|
|
}
|
|
else
|
|
major = cvtZToUInt(strPtr(version));
|
|
|
|
// No check to see if valid/supported PG version is on purpose
|
|
result = major * 10000 + minor * 100;
|
|
}
|
|
MEM_CONTEXT_TEMP_END();
|
|
|
|
FUNCTION_LOG_RETURN(UINT, result);
|
|
}
|
|
|
|
/***********************************************************************************************************************************
|
|
Convert version number to string
|
|
***********************************************************************************************************************************/
|
|
String *
|
|
pgVersionToStr(unsigned int version)
|
|
{
|
|
FUNCTION_LOG_BEGIN(logLevelTrace);
|
|
FUNCTION_LOG_PARAM(UINT, version);
|
|
FUNCTION_LOG_END();
|
|
|
|
String *result = version >= PG_VERSION_10 ?
|
|
strNewFmt("%u", version / 10000) : strNewFmt("%u.%u", version / 10000, version % 10000 / 100);
|
|
|
|
FUNCTION_LOG_RETURN(STRING, result);
|
|
}
|
|
|
|
/***********************************************************************************************************************************
|
|
Render as string for logging
|
|
***********************************************************************************************************************************/
|
|
String *
|
|
pgControlToLog(const PgControl *pgControl)
|
|
{
|
|
return strNewFmt(
|
|
"{version: %u, systemId: %" PRIu64 ", walSegmentSize: %u, pageChecksum: %s}", pgControl->version, pgControl->systemId,
|
|
pgControl->walSegmentSize, cvtBoolToConstZ(pgControl->pageChecksum));
|
|
}
|