diff --git a/doc/xml/release.xml b/doc/xml/release.xml index 7d59b3388..9dc0aaff3 100644 --- a/doc/xml/release.xml +++ b/doc/xml/release.xml @@ -30,6 +30,10 @@ Posix file functions now differentiate between open and missing errors. + + + Info objects now parse JSON and use specified storage. + diff --git a/src/Makefile b/src/Makefile index 52abbd469..b4609c9cb 100644 --- a/src/Makefile +++ b/src/Makefile @@ -84,6 +84,7 @@ SRCS = \ common/time.c \ common/type/buffer.c \ common/type/convert.c \ + common/type/json.c \ common/type/keyValue.c \ common/type/list.c \ common/type/string.c \ @@ -102,6 +103,9 @@ SRCS = \ crypto/hash.c \ crypto/crypto.c \ crypto/random.c \ + info/info.c \ + info/infoArchive.c \ + info/infoPg.c \ perl/config.c \ perl/exec.c \ postgres/info.c \ @@ -288,7 +292,7 @@ info/info.o: info/info.c common/debug.h common/error.auto.h common/error.h commo info/infoArchive.o: info/infoArchive.c common/debug.h common/error.auto.h common/error.h common/ini.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/memContext.h common/stackTrace.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 info/infoArchive.h info/infoPg.h storage/driver/posix/driverRead.h storage/driver/posix/driverWrite.h storage/fileRead.h storage/fileWrite.h storage/helper.h storage/info.h storage/storage.h version.h $(CC) $(CFLAGS) -c info/infoArchive.c -o info/infoArchive.o -info/infoPg.o: info/infoPg.c common/debug.h common/error.auto.h common/error.h common/ini.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/memContext.h common/regExp.h common/stackTrace.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 crypto/hash.h info/info.h info/infoPg.h postgres/info.h postgres/version.h storage/driver/posix/driverRead.h storage/driver/posix/driverWrite.h storage/fileRead.h storage/fileWrite.h storage/helper.h storage/info.h storage/storage.h version.h +info/infoPg.o: info/infoPg.c common/assert.h common/debug.h common/error.auto.h common/error.h common/ini.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/memContext.h common/stackTrace.h common/type/buffer.h common/type/convert.h common/type/json.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 crypto/hash.h info/info.h info/infoPg.h postgres/info.h postgres/version.h storage/driver/posix/driverRead.h storage/driver/posix/driverWrite.h storage/fileRead.h storage/fileWrite.h storage/helper.h storage/info.h storage/storage.h version.h $(CC) $(CFLAGS) -c info/infoPg.c -o info/infoPg.o main.o: main.c command/archive/get/get.h command/archive/push/push.h command/command.h command/help/help.h common/debug.h common/error.auto.h common/error.h common/exit.h common/lock.h common/log.h common/logLevel.h common/memContext.h common/stackTrace.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/load.h perl/exec.h version.h @@ -300,7 +304,7 @@ perl/config.o: perl/config.c common/debug.h common/error.auto.h common/error.h c perl/exec.o: perl/exec.c ../libc/LibC.h common/debug.h common/encode.h common/error.auto.h common/error.h common/io/filter/filter.h common/lock.h common/log.h common/logLevel.h common/memContext.h common/stackTrace.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/load.h config/parse.h crypto/cipher.h crypto/cipherBlock.h crypto/hash.h crypto/random.h perl/config.h perl/embed.auto.c perl/exec.h perl/libc.auto.c postgres/pageChecksum.h storage/driver/posix/driver.h storage/driver/posix/driverRead.h storage/driver/posix/driverWrite.h storage/info.h version.h ../libc/xs/common/encode.xsh ../libc/xs/crypto/cipherBlock.xsh ../libc/xs/crypto/hash.xsh $(CC) $(CFLAGS) -c perl/exec.c -o perl/exec.o -postgres/info.o: postgres/info.c 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/memContext.h common/stackTrace.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 postgres/info.h postgres/type.h postgres/version.h storage/driver/posix/driverRead.h storage/driver/posix/driverWrite.h storage/fileRead.h storage/fileWrite.h storage/helper.h storage/info.h storage/storage.h version.h +postgres/info.o: postgres/info.c 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/memContext.h common/regExp.h common/stackTrace.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 postgres/info.h postgres/type.h postgres/version.h storage/driver/posix/driverRead.h storage/driver/posix/driverWrite.h storage/fileRead.h storage/fileWrite.h storage/helper.h storage/info.h storage/storage.h version.h $(CC) $(CFLAGS) -c postgres/info.c -o postgres/info.o postgres/pageChecksum.o: postgres/pageChecksum.c common/assert.h common/debug.h common/error.auto.h common/error.h common/log.h common/logLevel.h common/stackTrace.h common/type/convert.h postgres/pageChecksum.h postgres/type.h diff --git a/src/info/info.c b/src/info/info.c index 546eea8b0..60cebce88 100644 --- a/src/info/info.c +++ b/src/info/info.c @@ -173,6 +173,7 @@ Internal function to load the copy and check validity static bool loadInternal( Info *this, // Info object to load parsed buffer into + const Storage *storage, bool copyFile) // Is this the copy file? { FUNCTION_DEBUG_BEGIN(logLevelTrace); @@ -188,8 +189,8 @@ loadInternal( { String *fileName = copyFile ? strCat(strDup(this->fileName), INI_COPY_EXT) : this->fileName; - // ??? Need to replace storageLocal when able - Buffer *buffer = storageGetNP(storageNewReadP(storageLocal(), fileName, .ignoreMissing = true)); + // Attempt to load the file + Buffer *buffer = storageGetNP(storageNewReadP(storage, fileName, .ignoreMissing = true)); // If the file exists, parse and validate it if (buffer != NULL) @@ -214,6 +215,7 @@ Load an Info object ***********************************************************************************************************************************/ Info * infoNew( + const Storage *storage, const String *fileName) // Full path/filename to load { FUNCTION_DEBUG_BEGIN(logLevelDebug); @@ -234,13 +236,14 @@ infoNew( this->fileName = strDup(fileName); // Attempt to load the main file. If it does not exist or is invalid, try to load the copy. - if (!loadInternal(this, false)) + if (!loadInternal(this, storage, false)) { - if (!loadInternal(this, true)) + if (!loadInternal(this, storage, true)) { THROW_FMT( FileMissingError, "unable to open %s or %s", - strPtr(this->fileName), strPtr(strCat(strDup(this->fileName), INI_COPY_EXT))); + strPtr(storagePathNP(storage, this->fileName)), + strPtr(strCat(storagePathNP(storage, this->fileName), INI_COPY_EXT))); } } } diff --git a/src/info/info.h b/src/info/info.h index 703e107eb..bc19a4cdb 100644 --- a/src/info/info.h +++ b/src/info/info.h @@ -11,11 +11,12 @@ typedef struct Info Info; #include "common/ini.h" #include "crypto/hash.h" +#include "storage/storage.h" /*********************************************************************************************************************************** Constructor ***********************************************************************************************************************************/ -Info *infoNew(const String *fileName); +Info *infoNew(const Storage *storage, const String *fileName); /*********************************************************************************************************************************** Getters diff --git a/src/info/infoArchive.c b/src/info/infoArchive.c index 4e59e533c..577226d50 100644 --- a/src/info/infoArchive.c +++ b/src/info/infoArchive.c @@ -29,7 +29,7 @@ Create a new InfoArchive object // ??? Need loadFile parameter ***********************************************************************************************************************************/ InfoArchive * -infoArchiveNew(const String *fileName, bool ignoreMissing) +infoArchiveNew(const Storage *storage, const String *fileName, bool ignoreMissing) { FUNCTION_DEBUG_BEGIN(logLevelDebug); FUNCTION_DEBUG_PARAM(STRING, fileName); @@ -45,13 +45,27 @@ infoArchiveNew(const String *fileName, bool ignoreMissing) // Create object this = memNew(sizeof(InfoArchive)); this->memContext = MEM_CONTEXT_NEW(); - this->infoPg = infoPgNew(fileName, infoPgArchive); + + // Catch file missing error and add archive-specific hints before rethrowing + TRY_BEGIN() + { + this->infoPg = infoPgNew(storage, fileName, infoPgArchive); + } + CATCH(FileMissingError) + { + THROW_FMT( + FileMissingError, + "%s\n" + "HINT: archive.info does not exist but is required to push/get WAL segments.\n" + "HINT: is archive_command configured in postgresql.conf?\n" + "HINT: has a stanza-create been performed?\n" + "HINT: use --no-archive-check to disable archive checks during backup if you have an alternate archiving scheme.", + errorMessage()); + } + TRY_END(); // Store the archiveId for the current PG db-version db-id - InfoPgData currentPg = infoPgDataCurrent(this->infoPg); - - this->archiveId = strNew(""); - strCatFmt(this->archiveId, "%s-%u", strPtr(infoPgVersionToString(currentPg.version)), currentPg.id); + this->archiveId = infoPgArchiveId(this->infoPg, 0); } MEM_CONTEXT_NEW_END(); @@ -113,6 +127,21 @@ infoArchiveId(const InfoArchive *this) FUNCTION_TEST_RESULT(STRING, this->archiveId); } +/*********************************************************************************************************************************** +Get PostgreSQL info +***********************************************************************************************************************************/ +InfoPg * +infoArchivePg(const InfoArchive *this) +{ + FUNCTION_TEST_BEGIN(); + FUNCTION_TEST_PARAM(INFO_ARCHIVE, this); + + FUNCTION_TEST_ASSERT(this != NULL); + FUNCTION_TEST_END(); + + FUNCTION_TEST_RESULT(INFO_PG, this->infoPg); +} + /*********************************************************************************************************************************** Free the info ***********************************************************************************************************************************/ diff --git a/src/info/infoArchive.h b/src/info/infoArchive.h index 6632db1c9..a13783aeb 100644 --- a/src/info/infoArchive.h +++ b/src/info/infoArchive.h @@ -10,11 +10,18 @@ Object type typedef struct InfoArchive InfoArchive; #include "common/type/string.h" +#include "info/infoPg.h" +#include "storage/storage.h" + +/*********************************************************************************************************************************** +Archive info filename +***********************************************************************************************************************************/ +#define INFO_ARCHIVE_FILE "archive.info" /*********************************************************************************************************************************** Constructor ***********************************************************************************************************************************/ -InfoArchive *infoArchiveNew(const String *fileName, bool ignoreMissing); +InfoArchive *infoArchiveNew(const Storage *storage, const String *fileName, bool ignoreMissing); /*********************************************************************************************************************************** Functions @@ -25,6 +32,7 @@ void infoArchiveCheckPg(const InfoArchive *this, unsigned int pgVersion, uint64_ Getters ***********************************************************************************************************************************/ const String *infoArchiveId(const InfoArchive *this); +InfoPg *infoArchivePg(const InfoArchive *this); /*********************************************************************************************************************************** Destructor diff --git a/src/info/infoPg.c b/src/info/infoPg.c index 21b0a5bd4..35e065105 100644 --- a/src/info/infoPg.c +++ b/src/info/infoPg.c @@ -6,12 +6,13 @@ PostgreSQL Info Handler #include #include +#include "common/assert.h" #include "common/debug.h" #include "common/log.h" #include "common/memContext.h" #include "common/ini.h" #include "common/memContext.h" -#include "common/regExp.h" +#include "common/type/json.h" #include "common/type/list.h" #include "info/info.h" #include "info/infoPg.h" @@ -43,46 +44,6 @@ struct InfoPg Info *info; // Info contents }; -/*********************************************************************************************************************************** -Return the PostgreSQL version constant given a string -***********************************************************************************************************************************/ -unsigned int -infoPgVersionToUIntInternal(const String *version) -{ - FUNCTION_DEBUG_BEGIN(logLevelTrace); - FUNCTION_DEBUG_PARAM(STRING, version); - - FUNCTION_DEBUG_ASSERT(version != NULL); - FUNCTION_DEBUG_END(); - - unsigned int result = 0; - - MEM_CONTEXT_TEMP_BEGIN() - { - // If format is not number.number (9.4) or number only (10) then error - if (!regExpMatchOne(strNew("^[0-9]+[.]*[0-9]+$"), version)) - THROW_FMT(AssertError, "version %s format is invalid", strPtr(version)); - - size_t idxStart = (size_t)(strChr(version, '.')); - int minor = 0; - int major; - - // If there is a dot, then set the major and minor versions, else just the major - if ((int)idxStart != -1) - minor = atoi(strPtr(strSub(version, idxStart + 1))); - else - idxStart = strSize(version); - - major = atoi(strPtr(strSubN(version, 0, idxStart))); - - // No check to see if valid/supported PG version is on purpose - result = (unsigned int)((major * 10000) + (minor * 100)); - } - MEM_CONTEXT_TEMP_END(); - - FUNCTION_DEBUG_RESULT(UINT, result); -} - /*********************************************************************************************************************************** Load an InfoPg object ??? Need to consider adding the following parameters in order to throw errors @@ -92,7 +53,7 @@ Load an InfoPg object ??? Currently this assumes the file exists and loads data from it ***********************************************************************************************************************************/ InfoPg * -infoPgNew(const String *fileName, InfoPgType type) +infoPgNew(const Storage *storage, const String *fileName, InfoPgType type) { FUNCTION_DEBUG_BEGIN(logLevelDebug); FUNCTION_DEBUG_PARAM(STRING, fileName); @@ -107,40 +68,60 @@ infoPgNew(const String *fileName, InfoPgType type) // Create object this = memNew(sizeof(InfoPg)); this->memContext = MEM_CONTEXT_NEW(); - this->info = infoNew(fileName); + this->info = infoNew(storage, fileName); - Ini *infoPgIni = infoIni(this->info); - - // ??? need to get the history list from the file in ascending order (important for ensuring currentIndex set properly in - // infoPgAdd) but need json parser so for now just getting current + // Get the pg history list this->history = lstNew(sizeof(InfoPgData)); - InfoPgData infoPgData = {0}; - MEM_CONTEXT_TEMP_BEGIN() { - String *dbSection = strNew(INFO_SECTION_DB); - infoPgData.systemId = varUInt64Force(iniGet(infoPgIni, dbSection, strNew(INFO_KEY_DB_SYSTEM_ID))); - infoPgData.id = (unsigned int)varIntForce(iniGet(infoPgIni, dbSection, strNew(INFO_KEY_DB_ID))); + const Ini *infoPgIni = infoIni(this->info); + const String *pgHistorySection = strNew(INFO_SECTION_DB_HISTORY); + const StringList *pgHistoryKey = iniSectionKeyList(infoPgIni, pgHistorySection); + const Variant *idKey = varNewStr(strNew(INFO_KEY_DB_ID)); + const Variant *systemIdKey = varNewStr(strNew(INFO_KEY_DB_SYSTEM_ID)); + const Variant *versionKey = varNewStr(strNew(INFO_KEY_DB_VERSION)); - String *pgVersion = varStrForce(iniGet(infoPgIni, dbSection, strNew(INFO_KEY_DB_VERSION))); + // History must include at least one item or the file is corrupt + ASSERT(strLstSize(pgHistoryKey) > 0); - // ??? Temporary hack for removing the leading and trailing quotes from pgVersion until can get json parser - infoPgData.version = infoPgVersionToUIntInternal(strSubN(pgVersion, 1, strSize(pgVersion) - 2)); - - if ((type == infoPgBackup) || (type == infoPgManifest)) + // Iterate in reverse because we would like the most recent pg history to be in position 0. If we need to look at the + // history list at all we'll be iterating from newest to oldest and putting newest in position 0 makes for more natural + // looping. + for (unsigned int pgHistoryIdx = strLstSize(pgHistoryKey) - 1; pgHistoryIdx < strLstSize(pgHistoryKey); pgHistoryIdx--) { - infoPgData.catalogVersion = - (unsigned int)varIntForce(iniGet(infoPgIni, dbSection, strNew(INFO_KEY_DB_CATALOG_VERSION))); - infoPgData.controlVersion = - (unsigned int)varIntForce(iniGet(infoPgIni, dbSection, strNew(INFO_KEY_DB_CONTROL_VERSION))); + // Load JSON data into a KeyValue + const KeyValue *pgDataKv = jsonToKv( + varStr(iniGet(infoPgIni, pgHistorySection, strLstGet(pgHistoryKey, pgHistoryIdx)))); + + // Get db values that are common to all info files + InfoPgData infoPgData = + { + .id = cvtZToUInt(strPtr(strLstGet(pgHistoryKey, pgHistoryIdx))), + .version = pgVersionFromStr(varStr(kvGet(pgDataKv, versionKey))), + + // This is different in archive.info due to a typo that can't be fixed without a format version bump + .systemId = varUInt64Force(kvGet(pgDataKv, type == infoPgArchive ? idKey : systemIdKey)), + }; + + // Get values that are only in backup and manifest files. These are really vestigial since stanza-create verifies + // the control and catalog versions so there is no good reason to store them. However, for backward compatability + // we must write them at least, even if we give up reading them. + if (type == infoPgBackup || type == infoPgManifest) + { + const Variant *catalogVersionKey = varNewStr(strNew(INFO_KEY_DB_CATALOG_VERSION)); + const Variant *controlVersionKey = varNewStr(strNew(INFO_KEY_DB_CONTROL_VERSION)); + + infoPgData.catalogVersion = (unsigned int)varUInt64Force(kvGet(pgDataKv, catalogVersionKey)); + infoPgData.controlVersion = (unsigned int)varUInt64Force(kvGet(pgDataKv, controlVersionKey)); + } + else if (type != infoPgArchive) + THROW_FMT(AssertError, "invalid InfoPg type %u", type); + + infoPgAdd(this, &infoPgData); } - else if (type != infoPgArchive) - THROW_FMT(AssertError, "invalid InfoPg type %u", type); } MEM_CONTEXT_TEMP_END(); - - infoPgAdd(this, &infoPgData); } MEM_CONTEXT_NEW_END(); @@ -168,6 +149,40 @@ infoPgAdd(InfoPg *this, const InfoPgData *infoPgData) FUNCTION_DEBUG_RESULT(UINT, this->indexCurrent); } +/*********************************************************************************************************************************** +Construct archive id +***********************************************************************************************************************************/ +String * +infoPgArchiveId(const InfoPg *this, unsigned int pgDataIdx) +{ + FUNCTION_DEBUG_BEGIN(logLevelTrace); + FUNCTION_DEBUG_PARAM(INFO_PG, this); + FUNCTION_DEBUG_PARAM(UINT, pgDataIdx); + + FUNCTION_DEBUG_ASSERT(this != NULL); + FUNCTION_DEBUG_END(); + + InfoPgData pgData = infoPgData(this, pgDataIdx); + + FUNCTION_DEBUG_RESULT(STRING, strNewFmt("%s-%u", strPtr(pgVersionToStr(pgData.version)), pgData.id)); +} + +/*********************************************************************************************************************************** +Return a structure of the Postgres data from a specific index +***********************************************************************************************************************************/ +InfoPgData +infoPgData(const InfoPg *this, unsigned int pgDataIdx) +{ + FUNCTION_DEBUG_BEGIN(logLevelTrace); + FUNCTION_DEBUG_PARAM(INFO_PG, this); + FUNCTION_DEBUG_PARAM(UINT, pgDataIdx); + + FUNCTION_DEBUG_ASSERT(this != NULL); + FUNCTION_DEBUG_END(); + + FUNCTION_DEBUG_RESULT(INFO_PG_DATA, *((InfoPgData *)lstGet(this->history, pgDataIdx))); +} + /*********************************************************************************************************************************** Return a structure of the current Postgres data ***********************************************************************************************************************************/ @@ -180,20 +195,22 @@ infoPgDataCurrent(const InfoPg *this) FUNCTION_DEBUG_ASSERT(this != NULL); FUNCTION_DEBUG_END(); - FUNCTION_DEBUG_RESULT(INFO_PG_DATA, *((InfoPgData *)lstGet(this->history, this->indexCurrent))); + FUNCTION_DEBUG_RESULT(INFO_PG_DATA, infoPgData(this, this->indexCurrent)); } /*********************************************************************************************************************************** -Return a string representation of the PostgreSQL version +Return total Postgres data in the history ***********************************************************************************************************************************/ -String * -infoPgVersionToString(unsigned int version) +unsigned int +infoPgDataTotal(const InfoPg *this) { FUNCTION_DEBUG_BEGIN(logLevelTrace); - FUNCTION_DEBUG_PARAM(UINT, version); + FUNCTION_DEBUG_PARAM(INFO_PG, this); + + FUNCTION_DEBUG_ASSERT(this != NULL); FUNCTION_DEBUG_END(); - FUNCTION_DEBUG_RESULT(STRING, strNewFmt("%u.%u", ((unsigned int)(version / 10000)), ((version % 10000) / 100))); + FUNCTION_DEBUG_RESULT(UINT, lstSize(this->history)); } /*********************************************************************************************************************************** diff --git a/src/info/infoPg.h b/src/info/infoPg.h index eb5c190ae..d3d2b3eda 100644 --- a/src/info/infoPg.h +++ b/src/info/infoPg.h @@ -11,6 +11,8 @@ typedef struct InfoPg InfoPg; #include +#include "storage/storage.h" + /*********************************************************************************************************************************** Information about the PostgreSQL cluster ***********************************************************************************************************************************/ @@ -36,7 +38,7 @@ typedef enum /*********************************************************************************************************************************** Constructor ***********************************************************************************************************************************/ -InfoPg *infoPgNew(const String *fileName, InfoPgType type); +InfoPg *infoPgNew(const Storage *storage, const String *fileName, InfoPgType type); /*********************************************************************************************************************************** Functions @@ -46,8 +48,11 @@ unsigned int infoPgAdd(InfoPg *this, const InfoPgData *infoPgData); /*********************************************************************************************************************************** Getters ***********************************************************************************************************************************/ +String *infoPgArchiveId(const InfoPg *this, unsigned int pgDataIdx); + +InfoPgData infoPgData(const InfoPg *this, unsigned int pgDataIdx); InfoPgData infoPgDataCurrent(const InfoPg *this); -String *infoPgVersionToString(unsigned int version); +unsigned int infoPgDataTotal(const InfoPg *this); /*********************************************************************************************************************************** Destructor diff --git a/src/postgres/info.c b/src/postgres/info.c index baf03a44e..8642cfe56 100644 --- a/src/postgres/info.c +++ b/src/postgres/info.c @@ -4,6 +4,8 @@ PostgreSQL Info #include "common/debug.h" #include "common/log.h" #include "common/memContext.h" +#include "common/type/convert.h" +#include "common/regExp.h" #include "postgres/info.h" #include "postgres/type.h" #include "postgres/version.h" @@ -62,6 +64,63 @@ pgVersionMap(uint32_t controlVersion, uint32_t catalogVersion) FUNCTION_TEST_RESULT(UINT, result); } +/*********************************************************************************************************************************** +Convert version string to version number +***********************************************************************************************************************************/ +unsigned int +pgVersionFromStr(const String *version) +{ + FUNCTION_DEBUG_BEGIN(logLevelTrace); + FUNCTION_DEBUG_PARAM(STRING, version); + + FUNCTION_DEBUG_ASSERT(version != NULL); + FUNCTION_DEBUG_END(); + + unsigned int result = 0; + + MEM_CONTEXT_TEMP_BEGIN() + { + // If format is not number.number (9.4) or number only (10) then error + if (!regExpMatchOne(strNew("^[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_DEBUG_RESULT(UINT, result); +} + +/*********************************************************************************************************************************** +Convert version number to string +***********************************************************************************************************************************/ +String * +pgVersionToStr(unsigned int version) +{ + FUNCTION_DEBUG_BEGIN(logLevelTrace); + FUNCTION_DEBUG_PARAM(UINT, version); + FUNCTION_DEBUG_END(); + + String *result = version >= PG_VERSION_10 ? + strNewFmt("%u", version / 10000) : strNewFmt("%u.%u", version / 10000, version % 10000 / 100); + + FUNCTION_DEBUG_RESULT(STRING, result); +} + /*********************************************************************************************************************************** Get info from pg_control ***********************************************************************************************************************************/ diff --git a/src/postgres/info.h b/src/postgres/info.h index cff2dcd1e..81125ac41 100644 --- a/src/postgres/info.h +++ b/src/postgres/info.h @@ -24,6 +24,8 @@ typedef struct PgControlInfo Functions ***********************************************************************************************************************************/ PgControlInfo pgControlInfo(const String *pgPath); +unsigned int pgVersionFromStr(const String *version); +String *pgVersionToStr(unsigned int version); /*********************************************************************************************************************************** Macros for function logging diff --git a/test/define.yaml b/test/define.yaml index d705c5e7d..c52a93493 100644 --- a/test/define.yaml +++ b/test/define.yaml @@ -334,7 +334,7 @@ unit: test: # ---------------------------------------------------------------------------------------------------------------------------- - name: info - total: 2 + total: 3 coverage: postgres/info: full @@ -557,7 +557,7 @@ unit: # ---------------------------------------------------------------------------------------------------------------------------- - name: info-pg - total: 2 + total: 1 coverage: info/infoPg: full diff --git a/test/src/module/info/infoArchiveTest.c b/test/src/module/info/infoArchiveTest.c index 9eeb877f9..7befef344 100644 --- a/test/src/module/info/infoArchiveTest.c +++ b/test/src/module/info/infoArchiveTest.c @@ -16,12 +16,22 @@ testRun(void) String *content = NULL; String *fileName = strNewFmt("%s/test.ini", testPath()); + TEST_ERROR_FMT( + infoArchiveNew(storageLocal(), fileName, true), FileMissingError, + "unable to open %s/test.ini or %s/test.ini.copy\n" + "HINT: archive.info does not exist but is required to push/get WAL segments.\n" + "HINT: is archive_command configured in postgresql.conf?\n" + "HINT: has a stanza-create been performed?\n" + "HINT: use --no-archive-check to disable archive checks during backup if you have an alternate archiving scheme.", + testPath(), testPath()); + + //-------------------------------------------------------------------------------------------------------------------------- content = strNew ( "[backrest]\n" - "backrest-checksum=\"b34b238ce89d8e1365c9e392ce59e7b03342ceb9\"\n" + "backrest-checksum=\"1efa53e0611604ad7d833c5547eb60ff716e758c\"\n" "backrest-format=5\n" - "backrest-version=\"2.04dev\"\n" + "backrest-version=\"2.04\"\n" "\n" "[db]\n" "db-id=1\n" @@ -37,8 +47,9 @@ testRun(void) InfoArchive *info = NULL; - TEST_ASSIGN(info, infoArchiveNew(fileName, true), " new archive info"); + TEST_ASSIGN(info, infoArchiveNew(storageLocal(), fileName, true), " new archive info"); TEST_RESULT_STR(strPtr(infoArchiveId(info)), "9.4-1", " archiveId set"); + TEST_RESULT_PTR(infoArchivePg(info), info->infoPg, " infoPg set"); // Check PG version //-------------------------------------------------------------------------------------------------------------------------- diff --git a/test/src/module/info/infoPgTest.c b/test/src/module/info/infoPgTest.c index 080c5a2b0..abd08c377 100644 --- a/test/src/module/info/infoPgTest.c +++ b/test/src/module/info/infoPgTest.c @@ -19,9 +19,9 @@ testRun(void) content = strNew ( "[backrest]\n" - "backrest-checksum=\"b34b238ce89d8e1365c9e392ce59e7b03342ceb9\"\n" + "backrest-checksum=\"1efa53e0611604ad7d833c5547eb60ff716e758c\"\n" "backrest-format=5\n" - "backrest-version=\"2.04dev\"\n" + "backrest-version=\"2.04\"\n" "\n" "[db]\n" "db-id=1\n" @@ -36,7 +36,7 @@ testRun(void) InfoPg *infoPg = NULL; - TEST_ASSIGN(infoPg, infoPgNew(fileName, infoPgArchive), "new infoPg archive - load file"); + TEST_ASSIGN(infoPg, infoPgNew(storageLocal(), fileName, infoPgArchive), "new infoPg archive - load file"); TEST_RESULT_INT(lstSize(infoPg->history), 1, " history record added"); TEST_RESULT_INT(infoPg->indexCurrent, 0, " current index set"); @@ -47,17 +47,17 @@ testRun(void) TEST_RESULT_INT(infoPgData.systemId, 6569239123849665679, " system-id set"); TEST_RESULT_INT(infoPgData.catalogVersion, 0, " catalog-version not set"); TEST_RESULT_INT(infoPgData.controlVersion, 0, " control-version not set"); - - TEST_RESULT_STR(strPtr(infoPgVersionToString(infoPgData.version)), "9.4", " version conversion back to string"); + TEST_RESULT_INT(infoPgDataTotal(infoPg), 1, " check pg data total"); + TEST_RESULT_STR(strPtr(infoPgArchiveId(infoPg, 0)), "9.4-1", " check pg archive id"); // Backup info //-------------------------------------------------------------------------------------------------------------------------- content = strNew ( "[backrest]\n" - "backrest-checksum=\"fc1d9ca71ebf1f5562f1fd21c4959233c9d04b18\"\n" + "backrest-checksum=\"5c17df9523543f5283efdc3c5aa7eb933c63ea0b\"\n" "backrest-format=5\n" - "backrest-version=\"2.04dev\"\n" + "backrest-version=\"2.04\"\n" "\n" "[db]\n" "db-catalog-version=201409291\n" @@ -68,12 +68,12 @@ testRun(void) "\n" "[db:history]\n" "1={\"db-catalog-version\":201409291,\"db-control-version\":942,\"db-system-id\":6569239123849665679," - "\"db-version\":\"9.4\"}\n" + "\"db-version\":\"9.4\"}\n" ); TEST_RESULT_VOID(storagePutNP(storageNewWriteNP(storageLocalWrite(), fileName), bufNewStr(content)), "put info to file"); - TEST_ASSIGN(infoPg, infoPgNew(fileName, infoPgBackup), "new infoPg backup - load file"); + TEST_ASSIGN(infoPg, infoPgNew(storageLocal(), fileName, infoPgBackup), "new infoPg backup - load file"); TEST_RESULT_INT(lstSize(infoPg->history), 1, " history record added"); TEST_RESULT_INT(infoPg->indexCurrent, 0, " current index set"); @@ -87,7 +87,7 @@ testRun(void) // Manifest info //-------------------------------------------------------------------------------------------------------------------------- - TEST_ASSIGN(infoPg, infoPgNew(fileName, infoPgManifest), "new infoPg manifest - load file"); + TEST_ASSIGN(infoPg, infoPgNew(storageLocal(), fileName, infoPgManifest), "new infoPg manifest - load file"); TEST_RESULT_INT(lstSize(infoPg->history), 1, "history record added"); TEST_RESULT_INT(infoPg->indexCurrent, 0, "current index set"); @@ -117,8 +117,9 @@ testRun(void) // Errors //-------------------------------------------------------------------------------------------------------------------------- - TEST_ERROR(infoPgNew(fileName, 10), AssertError, "invalid InfoPg type 10"); - TEST_ERROR(infoPgNew(NULL, infoPgManifest), AssertError, "function debug assertion 'fileName != NULL' failed"); + TEST_ERROR(infoPgNew(storageLocal(), fileName, 10), AssertError, "invalid InfoPg type 10"); + TEST_ERROR( + infoPgNew(storageLocal(), NULL, infoPgManifest), AssertError, "function debug assertion 'fileName != NULL' failed"); TEST_ERROR(infoPgDataCurrent(NULL), AssertError, "function debug assertion 'this != NULL' failed"); @@ -150,32 +151,4 @@ testRun(void) "{\"id: 4294967295, version: 4294967295, systemId 18446744073709551615, catalog 4294967295, control 4294967295\"}", " check max format"); } - - // ***************************************************************************************************************************** - if (testBegin("infoPgVersionToUIntInternal(), infoPgVersionToString()")) - { - String *version = NULL; - - // infoPgVersionToUIntInternal - //-------------------------------------------------------------------------------------------------------------------------- - version = strNew("10"); - TEST_RESULT_INT(infoPgVersionToUIntInternal(version), PG_VERSION_10, "Valid pg version 10 integer identifier"); - - // Internal function - doesn't check for validity since requested not to - version = strNew("15"); - TEST_RESULT_INT(infoPgVersionToUIntInternal(version), 150000, "version 15 is converted"); - - version = strNew("9.3.4"); - TEST_ERROR(infoPgVersionToUIntInternal(version), AssertError, "version 9.3.4 format is invalid"); - - version = strNew("abc"); - TEST_ERROR(infoPgVersionToUIntInternal(version), AssertError, "version abc format is invalid"); - TEST_ERROR(infoPgVersionToUIntInternal(NULL), AssertError, "function debug assertion 'version != NULL' failed"); - - // infoPgVersionToString - //-------------------------------------------------------------------------------------------------------------------------- - TEST_RESULT_STR(strPtr(infoPgVersionToString(PG_VERSION_11)), "11.0", "infoPgVersionToString 11.0"); - TEST_RESULT_STR(strPtr(infoPgVersionToString(PG_VERSION_96)), "9.6", "infoPgVersionToString 9.6"); - TEST_RESULT_STR(strPtr(infoPgVersionToString(123456)), "12.34", "infoPgVersionToString 123456"); - } } diff --git a/test/src/module/info/infoTest.c b/test/src/module/info/infoTest.c index 820615ac0..f78465367 100644 --- a/test/src/module/info/infoTest.c +++ b/test/src/module/info/infoTest.c @@ -21,9 +21,9 @@ testRun(void) content = strNew ( "[backrest]\n" - "backrest-checksum=\"b34b238ce89d8e1365c9e392ce59e7b03342ceb9\"\n" + "backrest-checksum=\"1efa53e0611604ad7d833c5547eb60ff716e758c\"\n" "backrest-format=5\n" - "backrest-version=\"2.04dev\"\n" + "backrest-version=\"2.04\"\n" "\n" "[db]\n" "db-id=1\n" @@ -38,14 +38,14 @@ testRun(void) //-------------------------------------------------------------------------------------------------------------------------- String *missingInfoError = strNewFmt("unable to open %s or %s", strPtr(fileName), strPtr(fileNameCopy)); - TEST_ERROR(infoNew(fileName), FileMissingError, strPtr(missingInfoError)); + TEST_ERROR(infoNew(storageLocal(), fileName), FileMissingError, strPtr(missingInfoError)); // Only copy exists and one is required //-------------------------------------------------------------------------------------------------------------------------- TEST_RESULT_VOID( storagePutNP(storageNewWriteNP(storageLocalWrite(), fileNameCopy), bufNewStr(content)), "put info.copy to file"); - TEST_ASSIGN(info, infoNew(fileName), "infoNew() - load copy file"); + TEST_ASSIGN(info, infoNew(storageLocal(), fileName), "infoNew() - load copy file"); TEST_RESULT_STR(strPtr(infoFileName(info)), strPtr(fileName), " infoFileName() is set"); TEST_RESULT_PTR(infoIni(info), info->ini, " infoIni() returns pointer to info->ini"); @@ -55,7 +55,7 @@ testRun(void) storageMoveNP(storageNewReadNP(storageLocal(), fileNameCopy), storageNewWriteNP(storageLocalWrite(), fileName)); // Only main info exists and is required - TEST_ASSIGN(info, infoNew(fileName), "infoNew() - load file"); + TEST_ASSIGN(info, infoNew(storageLocal(), fileName), "infoNew() - load file"); TEST_RESULT_STR(strPtr(infoFileName(info)), strPtr(fileName), " infoFileName() is set"); @@ -66,9 +66,9 @@ testRun(void) content = strNew ( "[backrest]\n" - "backrest-checksum=\"3ad6cbfc41984548747c65498a5079be96a4e4ef\"\n" + "backrest-checksum=\"14617b089cb5c9b3224e739bb794e865b9bcdf4b\"\n" "backrest-format=4\n" - "backrest-version=\"2.04dev\"\n" + "backrest-version=\"2.04\"\n" "\n" "[db]\n" "db-catalog-version=201409291\n" @@ -86,7 +86,7 @@ testRun(void) TEST_RESULT_VOID( storagePutNP(storageNewWriteNP(storageLocalWrite(), fileName), bufNewStr(content)), "put invalid br format to file"); - TEST_ERROR(infoNew(fileName), FileMissingError, strPtr(missingInfoError)); + TEST_ERROR(infoNew(storageLocal(), fileName), FileMissingError, strPtr(missingInfoError)); harnessLogResult( strPtr( strNewFmt("P00 WARN: invalid format in '%s', expected %d but found %d", strPtr(fileName), PGBACKREST_FORMAT, 4))); @@ -94,7 +94,7 @@ testRun(void) storageCopyNP(storageNewReadNP(storageLocal(), fileName), storageNewWriteNP(storageLocalWrite(), fileNameCopy)); TEST_ERROR( - infoNew(fileName), FormatError, + infoNew(storageLocal(), fileName), FormatError, strPtr(strNewFmt("invalid format in '%s', expected %d but found %d", strPtr(fileName), PGBACKREST_FORMAT, 4))); harnessLogResult( strPtr( @@ -149,7 +149,7 @@ testRun(void) // Copy file error TEST_ERROR( - infoNew(fileName), ChecksumError, + infoNew(storageLocal(), fileName), ChecksumError, strPtr(strNewFmt("invalid checksum in '%s', expected '%s' but found '%s'", strPtr(fileName), "4306ec205f71417c301e403c4714090e61c8a736", "4306ec205f71417c301e403c4714090e61c8a999"))); diff --git a/test/src/module/postgres/infoTest.c b/test/src/module/postgres/infoTest.c index aeaf6812e..e543072d7 100644 --- a/test/src/module/postgres/infoTest.c +++ b/test/src/module/postgres/infoTest.c @@ -44,6 +44,22 @@ testRun(void) TEST_ERROR_FMT(pgVersionMap(1100, 0), VersionNotSupportedError, MAP_ERROR, 1100); } + // ***************************************************************************************************************************** + if (testBegin("pgVersionFromStr() and pgVersionToStr()")) + { + TEST_ERROR(pgVersionFromStr(strNew("9.3.4")), AssertError, "version 9.3.4 format is invalid"); + TEST_ERROR(pgVersionFromStr(strNew("abc")), AssertError, "version abc format is invalid"); + TEST_ERROR(pgVersionFromStr(NULL), AssertError, "function debug assertion 'version != NULL' failed"); + + TEST_RESULT_INT(pgVersionFromStr(strNew("10")), PG_VERSION_10, "valid pg version 10"); + TEST_RESULT_INT(pgVersionFromStr(strNew("9.6")), 90600, "valid pg version 9.6"); + + //-------------------------------------------------------------------------------------------------------------------------- + TEST_RESULT_STR(strPtr(pgVersionToStr(PG_VERSION_11)), "11", "infoPgVersionToString 11"); + TEST_RESULT_STR(strPtr(pgVersionToStr(PG_VERSION_96)), "9.6", "infoPgVersionToString 9.6"); + TEST_RESULT_STR(strPtr(pgVersionToStr(83456)), "8.34", "infoPgVersionToString 83456"); + } + // ----------------------------------------------------------------------------------------------------------------------------- if (testBegin("pgControlInfo()")) {
Posix file functions now differentiate between open and missing errors.
Info objects now parse JSON and use specified storage.