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 date="XXXX-XX-XX" version="2.31dev" title="UNDER DEVELOPMENT">
|
||||
<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-item>
|
||||
<release-item-contributor-list>
|
||||
@ -9028,6 +9040,11 @@
|
||||
<contributor-id type="github">jmccormick2001</contributor-id>
|
||||
</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-name-display>Jens Wilke</contributor-name-display>
|
||||
<contributor-id type="github">jwpit</contributor-id>
|
||||
|
@ -358,19 +358,15 @@ iniLoad(
|
||||
|
||||
do
|
||||
{
|
||||
const String *line = strTrim(ioReadLineParam(read, true));
|
||||
const String *line = ioReadLineParam(read, true);
|
||||
const char *linePtr = strZ(line);
|
||||
|
||||
// Only interested in lines that are not blank or comments
|
||||
if (strSize(line) > 0 && linePtr[0] != '#')
|
||||
// Only interested in lines that are not blank
|
||||
if (strSize(line) > 0)
|
||||
{
|
||||
// Looks like this line is a section
|
||||
if (linePtr[0] == '[')
|
||||
// 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] == '[' && 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
|
||||
MEM_CONTEXT_PRIOR_BEGIN()
|
||||
{
|
||||
@ -378,7 +374,7 @@ iniLoad(
|
||||
}
|
||||
MEM_CONTEXT_PRIOR_END();
|
||||
}
|
||||
// Else it should be a key/value
|
||||
// Else it is a key/value
|
||||
else
|
||||
{
|
||||
if (section == NULL)
|
||||
@ -405,8 +401,8 @@ iniLoad(
|
||||
retry = false;
|
||||
|
||||
// Get key/value
|
||||
key = strTrim(strNewN(linePtr, (size_t)(lineEqual - linePtr)));
|
||||
value = strTrim(strNew(lineEqual + 1));
|
||||
key = strNewN(linePtr, (size_t)(lineEqual - linePtr));
|
||||
value = strNew(lineEqual + 1);
|
||||
|
||||
// Check that the value is valid JSON
|
||||
TRY_BEGIN()
|
||||
|
@ -27,7 +27,8 @@ Functions
|
||||
// Move to a new parent mem context
|
||||
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);
|
||||
|
||||
// Set an ini value
|
||||
@ -62,7 +63,9 @@ void iniFree(Ini *this);
|
||||
/***********************************************************************************************************************************
|
||||
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(
|
||||
IoRead *read,
|
||||
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(key != 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
|
||||
if (infoSaveData->sectionLast == NULL || !strEq(section, infoSaveData->sectionLast))
|
||||
|
@ -419,7 +419,7 @@ testBackupPqScript(unsigned int pgVersion, time_t backupTimeStart, TestBackupPqS
|
||||
// Start backup
|
||||
HRNPQ_MACRO_ADVISORY_LOCK(1, true),
|
||||
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"),
|
||||
|
||||
// Get copy start time
|
||||
|
@ -34,15 +34,6 @@ testRun(void)
|
||||
TEST_RESULT_VOID(iniLoad(ioBufferReadNew(iniBuf), testIniLoadCallback, result), "load 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
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
iniBuf = BUFSTRZ(
|
||||
@ -84,9 +75,8 @@ testRun(void)
|
||||
TEST_TITLE("one section");
|
||||
|
||||
iniBuf = BUFSTRZ(
|
||||
"# comment\n"
|
||||
"[section1]\n"
|
||||
"key1=\"value1\"\n"
|
||||
" key1 =\"value1\"\n"
|
||||
"key2=\"value2\"\n"
|
||||
"key=3==\"value3\"\n"
|
||||
"==\"=\"");
|
||||
@ -95,7 +85,7 @@ testRun(void)
|
||||
TEST_RESULT_VOID(iniLoad(ioBufferReadNew(iniBuf), testIniLoadCallback, result), "load ini");
|
||||
TEST_RESULT_STR_Z(
|
||||
result,
|
||||
"section1:key1:\"value1\"\n"
|
||||
"section1: key1 :\"value1\"\n"
|
||||
"section1:key2:\"value2\"\n"
|
||||
"section1:key=3=:\"value3\"\n"
|
||||
"section1:=:\"=\"\n",
|
||||
@ -104,22 +94,21 @@ testRun(void)
|
||||
// Two sections
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
iniBuf = BUFSTRZ(
|
||||
"# comment\n"
|
||||
"[section1]\n"
|
||||
"key1=\"value1\"\n"
|
||||
"[key1=\"value1\"\n"
|
||||
"key2=\"value2\"\n"
|
||||
"\n"
|
||||
"[section2]\n"
|
||||
"\n"
|
||||
"key2=\"value2\"");
|
||||
"#key2=\"value2\"");
|
||||
result = strNew("");
|
||||
|
||||
TEST_RESULT_VOID(iniLoad(ioBufferReadNew(iniBuf), testIniLoadCallback, result), "load ini");
|
||||
TEST_RESULT_STR_Z(
|
||||
result,
|
||||
"section1:key1:\"value1\"\n"
|
||||
"section1:[key1:\"value1\"\n"
|
||||
"section1:key2:\"value2\"\n"
|
||||
"section2:key2:\"value2\"\n",
|
||||
"section2:#key2:\"value2\"\n",
|
||||
" check ini");
|
||||
}
|
||||
|
||||
|
@ -1714,8 +1714,10 @@ testRun(void)
|
||||
#define TEST_MANIFEST_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" \
|
||||
"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" \
|
||||
"template0={\"db-id\":12168,\"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
|
||||
"\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"
|
||||
"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"
|
||||
TEST_MANIFEST_FILE
|
||||
TEST_MANIFEST_FILE_DEFAULT
|
||||
|
Reference in New Issue
Block a user