You've already forked pgbackrest
mirror of
https://github.com/pgbackrest/pgbackrest.git
synced 2025-07-15 01:04:37 +02:00
Allow [, #, and space as the first character in database names.
iniLoad() was trimming lines which meant that a leading space would not pass checksum validation when a manifest was reloaded. Remove the trims since files we write should never contain extraneous spaces. This further diverges the format for the functions that read conf files (e.g. pgbackrest.conf) and those that read info (e.g. manifest) files. While we are at it also allow [ and # as initial characters. # was reserved for comments but we never put comments into info files. [ denotes a section but we can get around this by never allowing arrays as values in info files, so if a line ends in ] it must be a section. This is currently the case but enforce it by adding an assert to info/info.c.
This commit is contained in:
@ -14,6 +14,18 @@
|
|||||||
<release-list>
|
<release-list>
|
||||||
<release date="XXXX-XX-XX" version="2.31dev" title="UNDER DEVELOPMENT">
|
<release date="XXXX-XX-XX" version="2.31dev" title="UNDER DEVELOPMENT">
|
||||||
<release-core-list>
|
<release-core-list>
|
||||||
|
<release-bug-list>
|
||||||
|
<release-item>
|
||||||
|
<release-item-contributor-list>
|
||||||
|
<release-item-ideator id="jefferson.alexandre"/>
|
||||||
|
<release-item-reviewer id="stefan.fercot"/>
|
||||||
|
<release-item-reviewer id="cynthia.shang"/>
|
||||||
|
</release-item-contributor-list>
|
||||||
|
|
||||||
|
<p>Allow <code>[</code>, <code>#</code>, and <code>space</code> as the first character in database names.</p>
|
||||||
|
</release-item>
|
||||||
|
</release-bug-list>
|
||||||
|
|
||||||
<release-feature-list>
|
<release-feature-list>
|
||||||
<release-item>
|
<release-item>
|
||||||
<release-item-contributor-list>
|
<release-item-contributor-list>
|
||||||
@ -9028,6 +9040,11 @@
|
|||||||
<contributor-id type="github">jmccormick2001</contributor-id>
|
<contributor-id type="github">jmccormick2001</contributor-id>
|
||||||
</contributor>
|
</contributor>
|
||||||
|
|
||||||
|
<contributor id="jefferson.alexandre">
|
||||||
|
<contributor-name-display>Jefferson Alexandre</contributor-name-display>
|
||||||
|
<contributor-id type="github">jalexandre0</contributor-id>
|
||||||
|
</contributor>
|
||||||
|
|
||||||
<contributor id="jens.wilke">
|
<contributor id="jens.wilke">
|
||||||
<contributor-name-display>Jens Wilke</contributor-name-display>
|
<contributor-name-display>Jens Wilke</contributor-name-display>
|
||||||
<contributor-id type="github">jwpit</contributor-id>
|
<contributor-id type="github">jwpit</contributor-id>
|
||||||
|
@ -358,19 +358,15 @@ iniLoad(
|
|||||||
|
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
const String *line = strTrim(ioReadLineParam(read, true));
|
const String *line = ioReadLineParam(read, true);
|
||||||
const char *linePtr = strZ(line);
|
const char *linePtr = strZ(line);
|
||||||
|
|
||||||
// Only interested in lines that are not blank or comments
|
// Only interested in lines that are not blank
|
||||||
if (strSize(line) > 0 && linePtr[0] != '#')
|
if (strSize(line) > 0)
|
||||||
{
|
{
|
||||||
// Looks like this line is a section
|
// The line is a section. Since the value must be valid JSON this means that the value must never be an array.
|
||||||
if (linePtr[0] == '[')
|
if (linePtr[0] == '[' && linePtr[strSize(line) - 1] == ']')
|
||||||
{
|
{
|
||||||
// Make sure the section ends with ]
|
|
||||||
if (linePtr[strSize(line) - 1] != ']')
|
|
||||||
THROW_FMT(FormatError, "ini section should end with ] at line %u: %s", lineIdx + 1, linePtr);
|
|
||||||
|
|
||||||
// Assign section
|
// Assign section
|
||||||
MEM_CONTEXT_PRIOR_BEGIN()
|
MEM_CONTEXT_PRIOR_BEGIN()
|
||||||
{
|
{
|
||||||
@ -378,7 +374,7 @@ iniLoad(
|
|||||||
}
|
}
|
||||||
MEM_CONTEXT_PRIOR_END();
|
MEM_CONTEXT_PRIOR_END();
|
||||||
}
|
}
|
||||||
// Else it should be a key/value
|
// Else it is a key/value
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (section == NULL)
|
if (section == NULL)
|
||||||
@ -405,8 +401,8 @@ iniLoad(
|
|||||||
retry = false;
|
retry = false;
|
||||||
|
|
||||||
// Get key/value
|
// Get key/value
|
||||||
key = strTrim(strNewN(linePtr, (size_t)(lineEqual - linePtr)));
|
key = strNewN(linePtr, (size_t)(lineEqual - linePtr));
|
||||||
value = strTrim(strNew(lineEqual + 1));
|
value = strNew(lineEqual + 1);
|
||||||
|
|
||||||
// Check that the value is valid JSON
|
// Check that the value is valid JSON
|
||||||
TRY_BEGIN()
|
TRY_BEGIN()
|
||||||
|
@ -27,7 +27,8 @@ Functions
|
|||||||
// Move to a new parent mem context
|
// Move to a new parent mem context
|
||||||
Ini *iniMove(Ini *this, MemContext *parentNew);
|
Ini *iniMove(Ini *this, MemContext *parentNew);
|
||||||
|
|
||||||
// Parse ini from a string
|
// Parse ini from a string. Comments are ignored and additional whitespace around sections, keys, and values is trimmed. Should be
|
||||||
|
// used *only* to read user-generated config files, for code-generated info files see iniLoad().
|
||||||
void iniParse(Ini *this, const String *content);
|
void iniParse(Ini *this, const String *content);
|
||||||
|
|
||||||
// Set an ini value
|
// Set an ini value
|
||||||
@ -62,7 +63,9 @@ void iniFree(Ini *this);
|
|||||||
/***********************************************************************************************************************************
|
/***********************************************************************************************************************************
|
||||||
Helper Functions
|
Helper Functions
|
||||||
***********************************************************************************************************************************/
|
***********************************************************************************************************************************/
|
||||||
// Load an ini file and return data to a callback
|
// Load an ini file and return data to a callback. Intended to read info files that were generated by code so do not have comments
|
||||||
|
// or extraneous spaces, and where all values are valid JSON. This allows syntax characters such as [, =, #, and whitespace to be
|
||||||
|
// used in keys.
|
||||||
void iniLoad(
|
void iniLoad(
|
||||||
IoRead *read,
|
IoRead *read,
|
||||||
void (*callbackFunction)(void *data, const String *section, const String *key, const String *value, const Variant *valueVar),
|
void (*callbackFunction)(void *data, const String *section, const String *key, const String *value, const Variant *valueVar),
|
||||||
|
@ -351,6 +351,9 @@ infoSaveValue(InfoSave *infoSaveData, const String *section, const String *key,
|
|||||||
ASSERT(section != NULL);
|
ASSERT(section != NULL);
|
||||||
ASSERT(key != NULL);
|
ASSERT(key != NULL);
|
||||||
ASSERT(jsonValue != NULL);
|
ASSERT(jsonValue != NULL);
|
||||||
|
ASSERT(strSize(jsonValue) != 0);
|
||||||
|
// The JSON value must not be an array because this may be confused with a section in the ini file
|
||||||
|
ASSERT(strZ(jsonValue)[0] != '[');
|
||||||
|
|
||||||
// Save section
|
// Save section
|
||||||
if (infoSaveData->sectionLast == NULL || !strEq(section, infoSaveData->sectionLast))
|
if (infoSaveData->sectionLast == NULL || !strEq(section, infoSaveData->sectionLast))
|
||||||
|
@ -419,7 +419,7 @@ testBackupPqScript(unsigned int pgVersion, time_t backupTimeStart, TestBackupPqS
|
|||||||
// Start backup
|
// Start backup
|
||||||
HRNPQ_MACRO_ADVISORY_LOCK(1, true),
|
HRNPQ_MACRO_ADVISORY_LOCK(1, true),
|
||||||
HRNPQ_MACRO_START_BACKUP_GE_10(1, param.startFast, lsnStartStr, walSegmentStart),
|
HRNPQ_MACRO_START_BACKUP_GE_10(1, param.startFast, lsnStartStr, walSegmentStart),
|
||||||
HRNPQ_MACRO_DATABASE_LIST_1(1, "test1"),
|
HRNPQ_MACRO_DATABASE_LIST_1(1, " test1"),
|
||||||
HRNPQ_MACRO_TABLESPACE_LIST_1(1, 32768, "tblspc32768"),
|
HRNPQ_MACRO_TABLESPACE_LIST_1(1, 32768, "tblspc32768"),
|
||||||
|
|
||||||
// Get copy start time
|
// Get copy start time
|
||||||
|
@ -34,15 +34,6 @@ testRun(void)
|
|||||||
TEST_RESULT_VOID(iniLoad(ioBufferReadNew(iniBuf), testIniLoadCallback, result), "load ini");
|
TEST_RESULT_VOID(iniLoad(ioBufferReadNew(iniBuf), testIniLoadCallback, result), "load ini");
|
||||||
TEST_RESULT_STR_Z(result, "", " check ini");
|
TEST_RESULT_STR_Z(result, "", " check ini");
|
||||||
|
|
||||||
// Invalid section
|
|
||||||
// -------------------------------------------------------------------------------------------------------------------------
|
|
||||||
iniBuf = BUFSTRZ(
|
|
||||||
" [section ");
|
|
||||||
|
|
||||||
TEST_ERROR(
|
|
||||||
iniLoad(ioBufferReadNew(iniBuf), testIniLoadCallback, result), FormatError,
|
|
||||||
"ini section should end with ] at line 1: [section");
|
|
||||||
|
|
||||||
// Key outside of section
|
// Key outside of section
|
||||||
// -------------------------------------------------------------------------------------------------------------------------
|
// -------------------------------------------------------------------------------------------------------------------------
|
||||||
iniBuf = BUFSTRZ(
|
iniBuf = BUFSTRZ(
|
||||||
@ -84,9 +75,8 @@ testRun(void)
|
|||||||
TEST_TITLE("one section");
|
TEST_TITLE("one section");
|
||||||
|
|
||||||
iniBuf = BUFSTRZ(
|
iniBuf = BUFSTRZ(
|
||||||
"# comment\n"
|
|
||||||
"[section1]\n"
|
"[section1]\n"
|
||||||
"key1=\"value1\"\n"
|
" key1 =\"value1\"\n"
|
||||||
"key2=\"value2\"\n"
|
"key2=\"value2\"\n"
|
||||||
"key=3==\"value3\"\n"
|
"key=3==\"value3\"\n"
|
||||||
"==\"=\"");
|
"==\"=\"");
|
||||||
@ -95,7 +85,7 @@ testRun(void)
|
|||||||
TEST_RESULT_VOID(iniLoad(ioBufferReadNew(iniBuf), testIniLoadCallback, result), "load ini");
|
TEST_RESULT_VOID(iniLoad(ioBufferReadNew(iniBuf), testIniLoadCallback, result), "load ini");
|
||||||
TEST_RESULT_STR_Z(
|
TEST_RESULT_STR_Z(
|
||||||
result,
|
result,
|
||||||
"section1:key1:\"value1\"\n"
|
"section1: key1 :\"value1\"\n"
|
||||||
"section1:key2:\"value2\"\n"
|
"section1:key2:\"value2\"\n"
|
||||||
"section1:key=3=:\"value3\"\n"
|
"section1:key=3=:\"value3\"\n"
|
||||||
"section1:=:\"=\"\n",
|
"section1:=:\"=\"\n",
|
||||||
@ -104,22 +94,21 @@ testRun(void)
|
|||||||
// Two sections
|
// Two sections
|
||||||
// -------------------------------------------------------------------------------------------------------------------------
|
// -------------------------------------------------------------------------------------------------------------------------
|
||||||
iniBuf = BUFSTRZ(
|
iniBuf = BUFSTRZ(
|
||||||
"# comment\n"
|
|
||||||
"[section1]\n"
|
"[section1]\n"
|
||||||
"key1=\"value1\"\n"
|
"[key1=\"value1\"\n"
|
||||||
"key2=\"value2\"\n"
|
"key2=\"value2\"\n"
|
||||||
"\n"
|
"\n"
|
||||||
"[section2]\n"
|
"[section2]\n"
|
||||||
"\n"
|
"\n"
|
||||||
"key2=\"value2\"");
|
"#key2=\"value2\"");
|
||||||
result = strNew("");
|
result = strNew("");
|
||||||
|
|
||||||
TEST_RESULT_VOID(iniLoad(ioBufferReadNew(iniBuf), testIniLoadCallback, result), "load ini");
|
TEST_RESULT_VOID(iniLoad(ioBufferReadNew(iniBuf), testIniLoadCallback, result), "load ini");
|
||||||
TEST_RESULT_STR_Z(
|
TEST_RESULT_STR_Z(
|
||||||
result,
|
result,
|
||||||
"section1:key1:\"value1\"\n"
|
"section1:[key1:\"value1\"\n"
|
||||||
"section1:key2:\"value2\"\n"
|
"section1:key2:\"value2\"\n"
|
||||||
"section2:key2:\"value2\"\n",
|
"section2:#key2:\"value2\"\n",
|
||||||
" check ini");
|
" check ini");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1714,8 +1714,10 @@ testRun(void)
|
|||||||
#define TEST_MANIFEST_DB \
|
#define TEST_MANIFEST_DB \
|
||||||
"\n" \
|
"\n" \
|
||||||
"[db]\n" \
|
"[db]\n" \
|
||||||
|
" mail\t={\"db-id\":16456,\"db-last-system-id\":12168}\n" \
|
||||||
|
"#={\"db-id\":16453,\"db-last-system-id\":12168}\n" \
|
||||||
"=={\"db-id\":16455,\"db-last-system-id\":12168}\n" \
|
"=={\"db-id\":16455,\"db-last-system-id\":12168}\n" \
|
||||||
"mail={\"db-id\":16456,\"db-last-system-id\":12168}\n" \
|
"[={\"db-id\":16454,\"db-last-system-id\":12168}\n" \
|
||||||
"postgres={\"db-id\":12173,\"db-last-system-id\":12168}\n" \
|
"postgres={\"db-id\":12173,\"db-last-system-id\":12168}\n" \
|
||||||
"template0={\"db-id\":12168,\"db-last-system-id\":12168}\n" \
|
"template0={\"db-id\":12168,\"db-last-system-id\":12168}\n" \
|
||||||
"template1={\"db-id\":1,\"db-last-system-id\":12168}\n" \
|
"template1={\"db-id\":1,\"db-last-system-id\":12168}\n" \
|
||||||
@ -1813,8 +1815,10 @@ testRun(void)
|
|||||||
TEST_MANIFEST_TARGET
|
TEST_MANIFEST_TARGET
|
||||||
"\n"
|
"\n"
|
||||||
"[db]\n"
|
"[db]\n"
|
||||||
|
" mail\t={\"db-id\":16456,\"db-last-system-id\":12168}\n"
|
||||||
|
"#={\"db-id\":16453,\"db-last-system-id\":12168}\n"
|
||||||
"=={\"db-id\":16455,\"db-last-system-id\":12168}\n"
|
"=={\"db-id\":16455,\"db-last-system-id\":12168}\n"
|
||||||
"mail={\"db-id\":16456,\"db-last-system-id\":12168}\n"
|
"[={\"db-id\":16454,\"db-last-system-id\":12168}\n"
|
||||||
"postgres={\"db-id\":12173,\"db-last-system-id\":12168}\n"
|
"postgres={\"db-id\":12173,\"db-last-system-id\":12168}\n"
|
||||||
TEST_MANIFEST_FILE
|
TEST_MANIFEST_FILE
|
||||||
TEST_MANIFEST_FILE_DEFAULT
|
TEST_MANIFEST_FILE_DEFAULT
|
||||||
|
Reference in New Issue
Block a user