From 97de12e58c18924b13bdf84633a7051a2a6e5ed7 Mon Sep 17 00:00:00 2001 From: Dimitri Fontaine Date: Fri, 2 Jun 2023 16:42:46 +0200 Subject: [PATCH] Fix history file parsing. (#995) The previous coding used a statically allocated array of pointers to newlines in that file, limiting our parsing abilities to files of 1024 lines maximum. Apparently that's not a good limit, so use dynamically allocated memory instead. Fixes #991. --- src/bin/pg_autoctl/pgsql.c | 17 +++++++++----- src/bin/pg_autoctl/string_utils.c | 37 +++++++++++++++++++++++++++++++ src/bin/pg_autoctl/string_utils.h | 1 + 3 files changed, 49 insertions(+), 6 deletions(-) diff --git a/src/bin/pg_autoctl/pgsql.c b/src/bin/pg_autoctl/pgsql.c index 4371167ae..450c7e1a5 100644 --- a/src/bin/pg_autoctl/pgsql.c +++ b/src/bin/pg_autoctl/pgsql.c @@ -3094,18 +3094,18 @@ bool parseTimeLineHistory(const char *filename, const char *content, IdentifySystem *system) { - char *historyLines[BUFSIZE] = { 0 }; - int lineCount = splitLines((char *) content, historyLines, BUFSIZE); + int lineCount = countLines((char *) content); + char **historyLines = (char **) calloc(lineCount, sizeof(char *)); int lineNumber = 0; - if (lineCount >= PG_AUTOCTL_MAX_TIMELINES) + if (historyLines == NULL) { - log_error("history file \"%s\" contains %d lines, " - "pg_autoctl only supports up to %d lines", - filename, lineCount, PG_AUTOCTL_MAX_TIMELINES - 1); + log_error(ALLOCATION_FAILED_ERROR); return false; } + splitLines((char *) content, historyLines, lineCount); + uint64_t prevend = InvalidXLogRecPtr; system->timelines.count = 0; @@ -3141,6 +3141,7 @@ parseTimeLineHistory(const char *filename, const char *content, { log_error("Failed to parse history file line %d: \"%s\"", lineNumber, ptr); + free(historyLines); return false; } @@ -3149,6 +3150,7 @@ parseTimeLineHistory(const char *filename, const char *content, if (!stringToUInt(historyLines[lineNumber], &(entry->tli))) { log_error("Failed to parse history timeline \"%s\"", tabptr); + free(historyLines); return false; } @@ -3167,6 +3169,7 @@ parseTimeLineHistory(const char *filename, const char *content, { log_error("Failed to parse history timeline %d LSN \"%s\"", entry->tli, lsn); + free(historyLines); return false; } @@ -3184,6 +3187,8 @@ parseTimeLineHistory(const char *filename, const char *content, entry = &(system->timelines.history[++system->timelines.count]); } + free(historyLines); + /* * Create one more entry for the "tip" of the timeline, which has no entry * in the history file. diff --git a/src/bin/pg_autoctl/string_utils.c b/src/bin/pg_autoctl/string_utils.c index 06d4112ac..c9d38f2c2 100644 --- a/src/bin/pg_autoctl/string_utils.c +++ b/src/bin/pg_autoctl/string_utils.c @@ -495,6 +495,43 @@ IntervalToString(double seconds, char *buffer, size_t size) } +/* + * countLines returns how many line separators (\n) are found in the given + * string. + */ +int +countLines(char *buffer) +{ + int lineNumber = 0; + char *currentLine = buffer; + + if (buffer == NULL) + { + return 0; + } + + do { + char *newLinePtr = strchr(currentLine, '\n'); + + if (newLinePtr == NULL) + { + if (strlen(currentLine) > 0) + { + ++lineNumber; + } + currentLine = NULL; + } + else + { + ++lineNumber; + currentLine = ++newLinePtr; + } + } while (currentLine != NULL && *currentLine != '\0'); + + return lineNumber; +} + + /* * splitLines prepares a multi-line error message in a way that calling code * can loop around one line at a time and call log_error() or log_warn() on diff --git a/src/bin/pg_autoctl/string_utils.h b/src/bin/pg_autoctl/string_utils.h index 89e31a26f..928e6defb 100644 --- a/src/bin/pg_autoctl/string_utils.h +++ b/src/bin/pg_autoctl/string_utils.h @@ -37,6 +37,7 @@ bool stringToUInt32(const char *str, uint32_t *number); bool stringToDouble(const char *str, double *number); bool IntervalToString(double seconds, char *buffer, size_t size); +int countLines(char *buffer); int splitLines(char *errorMessage, char **linesArray, int size); void processBufferCallback(const char *buffer, bool error);