From 9201f1f5a0a0f6d0edaec1d84570e2d4cb6d6981 Mon Sep 17 00:00:00 2001 From: Dianjin Wang Date: Sat, 12 Sep 2026 00:46:50 +0800 Subject: [PATCH] pg_basebackup: actually send the EXCLUDE options to the server build_exclude_list() assembled the EXCLUDE clauses into a buffer of its own, and BaseBackup() then freed that buffer without ever putting it in the BASE_BACKUP command -- so -E/--exclude and --exclude-from were silently ignored and every excluded path was copied anyway. Worse than the wasted copying: gpinitstandby passes -E ./promote, and 'promote' is the name of PostgreSQL's promotion signal file. Copying the coordinator's promote directory into the standby left a 'promote' entry there, CheckPromoteSignal()'s stat() succeeds on a directory, and the brand new standby coordinator promoted itself the moment it reached a consistent state -- picking a new timeline instead of streaming, so pg_stat_replication on the coordinator stayed empty. Append the options to the command buffer with AppendStringCommandOption() instead. The server has always understood a repeated EXCLUDE (parse_basebackup_options collects them into one hash table); only the client half was missing. Exercised by gpinitstandby.feature:33 'gpinitstandby exclude dirs'. Split out of #1639 (originally b432ad8d71b). --- src/bin/pg_basebackup/pg_basebackup.c | 60 ++++++++++----------------- 1 file changed, 23 insertions(+), 37 deletions(-) diff --git a/src/bin/pg_basebackup/pg_basebackup.c b/src/bin/pg_basebackup/pg_basebackup.c index 24cd2f05258..82d9f005008 100644 --- a/src/bin/pg_basebackup/pg_basebackup.c +++ b/src/bin/pg_basebackup/pg_basebackup.c @@ -1705,36 +1705,24 @@ get_tablespace_mapping(const char *dir) } static void -add_to_exclude_list(PQExpBufferData *buf, const char *exclude) +add_to_exclude_list(PQExpBuffer buf, bool use_new_option_syntax, + const char *exclude) { - char quoted[MAXPGPATH]; - int error; - size_t len; - - error = 1; - len = PQescapeStringConn(conn, quoted, exclude, MAXPGPATH, &error); - if (len == 0 || error != 0) - { - pg_log_error("could not process exclude \"%s\": %s", - exclude, PQerrorMessage(conn)); - exit(1); - } - appendPQExpBuffer(buf, " EXCLUDE '%s'", quoted); + /* + * EXCLUDE may be given more than once; the server collects every + * occurrence into one hash table. + */ + AppendStringCommandOption(buf, use_new_option_syntax, "EXCLUDE", + unconstify(char *, exclude)); } -static char * -build_exclude_list(void) +static void +append_exclude_options(PQExpBuffer buf, bool use_new_option_syntax) { - PQExpBufferData buf; int i; - if (num_exclude == 0 && num_exclude_from == 0) - return ""; - - initPQExpBuffer(&buf); - for (i = 0; i < num_exclude; i++) - add_to_exclude_list(&buf, excludes[i]); + add_to_exclude_list(buf, use_new_option_syntax, excludes[i]); for (i = 0; i < num_exclude_from; i++) { @@ -1763,19 +1751,11 @@ build_exclude_list(void) len--) str[len - 1] = '\0'; - add_to_exclude_list(&buf, str); + add_to_exclude_list(buf, use_new_option_syntax, str); } fclose(file); } - - if (PQExpBufferDataBroken(buf)) - { - pg_log_error("out of memory"); - exit(1); - } - - return buf.data; } /* @@ -1850,7 +1830,6 @@ BaseBackup(char *compression_algorithm, char *compression_detail, char xlogend[64] = {0}; int minServerMajor, maxServerMajor; - char *exclude_list; int serverVersion, serverMajor; int writing_to_stdout; @@ -1994,7 +1973,17 @@ BaseBackup(char *compression_algorithm, char *compression_detail, "COMPRESSION_DETAIL", compression_detail); } - exclude_list = build_exclude_list(); + append_exclude_options(&buf, use_new_option_syntax); + + /* + * The append helpers do not exit on allocation failure; they mark the + * buffer broken, which empties it and turns later appends into no-ops. + * Without this check an out-of-memory would send a BASE_BACKUP carrying + * none of the options built above -- including the EXCLUDEs -- instead of + * failing. + */ + if (PQExpBufferDataBroken(buf)) + pg_fatal("out of memory"); if (verbose) pg_log_info("initiating base backup, waiting for checkpoint to complete"); @@ -2008,9 +1997,6 @@ BaseBackup(char *compression_algorithm, char *compression_detail, fprintf(stderr, "\n"); } - if (exclude_list[0] != '\0') - free(exclude_list); - if (use_new_option_syntax && buf.len > 0) basebkp = psprintf("BASE_BACKUP (%s)", buf.data); else