diff --git a/doc/xml/release.xml b/doc/xml/release.xml
index 42f1bafd8..8ce3a77f3 100644
--- a/doc/xml/release.xml
+++ b/doc/xml/release.xml
@@ -90,6 +90,10 @@
Add pgControlInfo()
to read pg_control and determine the version.
+
+ Add walSegmentNext()
and walSegmentRange()
.
+
+
Add THROWP_
* macro variants for error handling. These macros allow an ErrorType
pointer to be passed and are required for functions that may return different errors based on a parameter.
diff --git a/src/command/archive/common.c b/src/command/archive/common.c
index 42048d494..56d1b76b6 100644
--- a/src/command/archive/common.c
+++ b/src/command/archive/common.c
@@ -1,12 +1,16 @@
/***********************************************************************************************************************************
Archive Push Command
***********************************************************************************************************************************/
+#include
+#include
#include
#include "command/archive/common.h"
+#include "common/assert.h"
#include "common/log.h"
#include "common/memContext.h"
#include "common/wait.h"
+#include "postgres/version.h"
#include "storage/helper.h"
/***********************************************************************************************************************************
@@ -94,3 +98,82 @@ archiveAsyncStatus(const String *walSegment, bool confessOnError)
return result;
}
+
+/***********************************************************************************************************************************
+Get the next WAL segment given a WAL segment and WAL segment size
+***********************************************************************************************************************************/
+String *
+walSegmentNext(const String *walSegment, size_t walSegmentSize, uint pgVersion)
+{
+ ASSERT_DEBUG(walSegment != NULL);
+ ASSERT_DEBUG(strSize(walSegment) == 24);
+ ASSERT_DEBUG(UINT32_MAX % walSegmentSize == walSegmentSize - 1);
+ ASSERT_DEBUG(pgVersion >= PG_VERSION_11 || walSegmentSize == 16 * 1024 * 1024);
+
+ // Extract WAL parts
+ uint32_t timeline = 0;
+ uint32_t major = 0;
+ uint32_t minor = 0;
+
+ MEM_CONTEXT_TEMP_BEGIN()
+ {
+ timeline = (uint32_t)strtol(strPtr(strSubN(walSegment, 0, 8)), NULL, 16);
+ major = (uint32_t)strtol(strPtr(strSubN(walSegment, 8, 8)), NULL, 16);
+ minor = (uint32_t)strtol(strPtr(strSubN(walSegment, 16, 8)), NULL, 16);
+
+ // Increment minor and adjust major dir on overflow
+ minor++;
+
+ if (minor > UINT32_MAX / walSegmentSize)
+ {
+ major++;
+ minor = 0;
+ }
+
+ // Special hack for PostgreSQL < 9.3 which skipped minor FF
+ if (minor == 0xFF && pgVersion < PG_VERSION_93)
+ {
+ major++;
+ minor = 0;
+ }
+ }
+ MEM_CONTEXT_TEMP_END();
+
+ return strNewFmt("%08X%08X%08X", timeline, major, minor);
+}
+
+/***********************************************************************************************************************************
+Build a list of WAL segments based on a beginning WAL and number of WAL in the range (inclusive)
+***********************************************************************************************************************************/
+StringList *
+walSegmentRange(const String *walSegmentBegin, size_t walSegmentSize, uint pgVersion, uint range)
+{
+ ASSERT_DEBUG(range > 0);
+
+ StringList *result = NULL;
+
+ MEM_CONTEXT_TEMP_BEGIN()
+ {
+ result = strLstAdd(strLstNew(), walSegmentBegin);
+
+ if (range > 1)
+ {
+ String *current = strDup(walSegmentBegin);
+
+ for (uint rangeIdx = 0; rangeIdx < range - 1; rangeIdx++)
+ {
+ String *next = walSegmentNext(current, walSegmentSize, pgVersion);
+
+ strLstAdd(result, next);
+
+ strFree(current);
+ current = next;
+ }
+ }
+
+ strLstMove(result, MEM_CONTEXT_OLD());
+ }
+ MEM_CONTEXT_TEMP_END();
+
+ return result;
+}
diff --git a/src/command/archive/common.h b/src/command/archive/common.h
index 45054a497..404c6a2a2 100644
--- a/src/command/archive/common.h
+++ b/src/command/archive/common.h
@@ -4,11 +4,15 @@ Archive Common
#ifndef COMMAND_ARCHIVE_COMMON_H
#define COMMAND_ARCHIVE_COMMON_H
-#include "common/type/string.h"
+#include
+
+#include "common/type/stringList.h"
/***********************************************************************************************************************************
Functions
***********************************************************************************************************************************/
bool archiveAsyncStatus(const String *walSegment, bool confessOnError);
+String *walSegmentNext(const String *walSegment, size_t walSegmentSize, uint pgVersion);
+StringList *walSegmentRange(const String *walSegmentBegin, size_t walSegmentSize, uint pgVersion, uint range);
#endif
diff --git a/test/define.yaml b/test/define.yaml
index 3a00b43a2..7cde400d9 100644
--- a/test/define.yaml
+++ b/test/define.yaml
@@ -506,7 +506,7 @@ unit:
test:
# ----------------------------------------------------------------------------------------------------------------------------
- name: common
- total: 1
+ total: 3
coverage:
command/archive/common: full
diff --git a/test/src/module/archive/commonTest.c b/test/src/module/archive/commonTest.c
index 62fa4c6d3..effdfdc47 100644
--- a/test/src/module/archive/commonTest.c
+++ b/test/src/module/archive/commonTest.c
@@ -87,4 +87,52 @@ testRun()
unlink(strPtr(storagePathNP(storageSpool(), strNewFmt(STORAGE_SPOOL_ARCHIVE_OUT "/%s.error", strPtr(segment)))));
}
+
+ // *****************************************************************************************************************************
+ if (testBegin("walSegmentNext()"))
+ {
+ TEST_RESULT_STR(
+ strPtr(walSegmentNext(strNew("000000010000000100000001"), 16 * 1024 * 1024, PG_VERSION_10)),
+ "000000010000000100000002", "get next");
+ TEST_RESULT_STR(
+ strPtr(walSegmentNext(strNew("0000000100000001000000FE"), 16 * 1024 * 1024, PG_VERSION_93)),
+ "0000000100000001000000FF", "get next");
+ TEST_RESULT_STR(
+ strPtr(walSegmentNext(strNew("0000009900000001000000FF"), 16 * 1024 * 1024, PG_VERSION_93)),
+ "000000990000000200000000", "get next overflow >= 9.3");
+ TEST_RESULT_STR(
+ strPtr(walSegmentNext(strNew("0000000100000001000000FE"), 16 * 1024 * 1024, PG_VERSION_92)),
+ "000000010000000200000000", "get next overflow < 9.3");
+ TEST_RESULT_STR(
+ strPtr(walSegmentNext(strNew("000000010000000100000003"), 1024 * 1024 * 1024, PG_VERSION_11)),
+ "000000010000000200000000", "get next overflow >= 11/1GB");
+ TEST_RESULT_STR(
+ strPtr(walSegmentNext(strNew("000000010000006700000FFF"), 1024 * 1024, PG_VERSION_11)),
+ "000000010000006800000000", "get next overflow >= 11/1MB");
+ }
+
+ // *****************************************************************************************************************************
+ if (testBegin("walSegmentRange()"))
+ {
+ TEST_RESULT_STR(
+ strPtr(strLstJoin(walSegmentRange(strNew("000000010000000100000000"), 16 * 1024 * 1024, PG_VERSION_92, 1), "|")),
+ "000000010000000100000000", "get single");
+ TEST_RESULT_STR(
+ strPtr(strLstJoin(walSegmentRange(strNew("0000000100000001000000FD"), 16 * 1024 * 1024, PG_VERSION_92, 4), "|")),
+ "0000000100000001000000FD|0000000100000001000000FE|000000010000000200000000|000000010000000200000001",
+ "get range < 9.3");
+ TEST_RESULT_STR(
+ strPtr(strLstJoin(walSegmentRange(strNew("0000000100000001000000FD"), 16 * 1024 * 1024, PG_VERSION_93, 4), "|")),
+ "0000000100000001000000FD|0000000100000001000000FE|0000000100000001000000FF|000000010000000200000000",
+ "get range >= 9.3");
+ TEST_RESULT_STR(
+ strPtr(strLstJoin(walSegmentRange(strNew("000000080000000A00000000"), 1024 * 1024 * 1024, PG_VERSION_11, 8), "|")),
+ "000000080000000A00000000|000000080000000A00000001|000000080000000A00000002|000000080000000A00000003|"
+ "000000080000000B00000000|000000080000000B00000001|000000080000000B00000002|000000080000000B00000003",
+ "get range >= 11/1GB");
+ TEST_RESULT_STR(
+ strPtr(strLstJoin(walSegmentRange(strNew("000000070000000700000FFE"), 1024 * 1024, PG_VERSION_11, 4), "|")),
+ "000000070000000700000FFE|000000070000000700000FFF|000000070000000800000000|000000070000000800000001",
+ "get range >= 11/1MB");
+ }
}