diff --git a/src/bin/common/pgsql.c b/src/bin/common/pgsql.c index 4fe32a084..e981aff89 100644 --- a/src/bin/common/pgsql.c +++ b/src/bin/common/pgsql.c @@ -3190,8 +3190,62 @@ parseTimeLineHistory(const char *filename, const char *content, system->timelines.count = 0; - TimeLineHistoryEntry *entry = - &(system->timelines.history[system->timelines.count]); + /* + * Ensure the history buffer exists and has room for at least lineCount + 1 + * entries (all parsed lines plus the tip entry written after the loop). + * We reuse the buffer across calls; realloc only when capacity is tight. + */ + int needed = lineCount + 1; + + if (system->timelines.history == NULL) + { + int cap = PG_AUTOCTL_TIMELINES_INITIAL_CAPACITY; + + while (cap < needed) + { + cap *= 2; + } + + system->timelines.history = + (TimeLineHistoryEntry *) calloc(cap, sizeof(TimeLineHistoryEntry)); + + if (system->timelines.history == NULL) + { + log_error(ALLOCATION_FAILED_ERROR); + free(historyLines); + return false; + } + + system->timelines.capacity = cap; + } + else if (system->timelines.capacity < needed) + { + int cap = system->timelines.capacity; + + while (cap < needed) + { + cap *= 2; + } + + TimeLineHistoryEntry *newHistory = + (TimeLineHistoryEntry *) realloc(system->timelines.history, + cap * sizeof(TimeLineHistoryEntry)); + + if (newHistory == NULL) + { + log_error(ALLOCATION_FAILED_ERROR); + free(historyLines); + return false; + } + + memset(newHistory + system->timelines.capacity, + 0, + (cap - system->timelines.capacity) * sizeof(TimeLineHistoryEntry)); + system->timelines.history = newHistory; + system->timelines.capacity = cap; + } + + TimeLineHistoryEntry *entry = &(system->timelines.history[0]); for (lineNumber = 0; lineNumber < lineCount; lineNumber++) { @@ -3271,7 +3325,7 @@ parseTimeLineHistory(const char *filename, const char *content, /* * Create one more entry for the "tip" of the timeline, which has no entry - * in the history file. + * in the history file. Capacity was pre-checked above to include this slot. */ entry->tli = system->timeline; entry->begin = prevend; diff --git a/src/bin/common/pgsql.h b/src/bin/common/pgsql.h index 978f6b172..2b51b2eda 100644 --- a/src/bin/common/pgsql.h +++ b/src/bin/common/pgsql.h @@ -193,7 +193,7 @@ typedef struct NodeAddressArray #define InvalidXLogRecPtr 0 #define XLogRecPtrIsInvalid(r) ((r) == InvalidXLogRecPtr) -#define PG_AUTOCTL_MAX_TIMELINES 1024 +#define PG_AUTOCTL_TIMELINES_INITIAL_CAPACITY 16 typedef struct TimeLineHistoryEntry { @@ -203,10 +203,19 @@ typedef struct TimeLineHistoryEntry } TimeLineHistoryEntry; +/* + * TimeLineHistory holds a dynamically-allocated array of timeline history + * entries. The history pointer starts as NULL and is allocated on first use + * inside parseTimeLineHistory(); subsequent calls reuse the same buffer, + * growing it with realloc() when needed. No explicit free is required because + * this struct is embedded in long-lived per-process structs (LocalPostgresServer + * → ReplicationSource → IdentifySystem) that live for the process lifetime. + */ typedef struct TimeLineHistory { int count; - TimeLineHistoryEntry history[PG_AUTOCTL_MAX_TIMELINES]; + int capacity; + TimeLineHistoryEntry *history; } TimeLineHistory;