Allow dbname to be written as part of connstring via pg_basebackup's -R option.

Commit cca97ce6a6 allowed dbname in pg_basebackup connstring and in this
commit we allow it to be written in postgresql.auto.conf when -R option is
used. The database name in the connection string will be used by the
logical replication slot synchronization on standby.

The dbname will be recorded only if specified explicitly in the connection
string or environment variable.

Masahiko Sawada hasn't reviewed the code in detail but endorsed the idea.

Author: Vignesh C, Kuroda Hayato
Reviewed-by: Amit Kapila
Discussion: https://postgr.es/m/CAB8KJ=hdKdg+UeXhReeHpHA6N6v3e0qFF+ZsPFHk9_ThWKf=2A@mail.gmail.com
This commit is contained in:
Amit Kapila 2024-03-21 10:48:59 +05:30
parent 30e144287a
commit a145f424d5
8 changed files with 127 additions and 8 deletions

View File

@ -243,7 +243,11 @@ PostgreSQL documentation
The <filename>postgresql.auto.conf</filename> file will record the connection The <filename>postgresql.auto.conf</filename> file will record the connection
settings and, if specified, the replication slot settings and, if specified, the replication slot
that <application>pg_basebackup</application> is using, so that that <application>pg_basebackup</application> is using, so that
streaming replication will use the same settings later on. streaming replication and <link linkend="logicaldecoding-replication-slots-synchronization">
logical replication slot synchronization</link> will use the same
settings later on. The dbname will be recorded only if the dbname was
specified explicitly in the connection string or <link linkend="libpq-envars">
environment variable</link>.
</para> </para>
</listitem> </listitem>
@ -809,7 +813,9 @@ PostgreSQL documentation
name in the connection string will be ignored name in the connection string will be ignored
by <productname>PostgreSQL</productname>. Middleware, or proxies, used in by <productname>PostgreSQL</productname>. Middleware, or proxies, used in
connecting to <productname>PostgreSQL</productname> might however connecting to <productname>PostgreSQL</productname> might however
utilize the value. utilize the value. The database name specified in connection string can
also be used by <link linkend="logicaldecoding-replication-slots-synchronization">
logical replication slot synchronization</link>.
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>

View File

@ -1807,10 +1807,18 @@ BaseBackup(char *compression_algorithm, char *compression_detail,
} }
/* /*
* Build contents of configuration file if requested * Build contents of configuration file if requested.
*
* Note that we don't use the dbname from key-value pair in conn as that
* would have been filled by the default dbname (dbname=replication) in
* case the user didn't specify the one. The dbname written in the config
* file as part of primary_conninfo would be used by slotsync worker which
* doesn't use a replication connection so the default won't work for it.
*/ */
if (writerecoveryconf) if (writerecoveryconf)
recoveryconfcontents = GenerateRecoveryConfig(conn, replication_slot); recoveryconfcontents = GenerateRecoveryConfig(conn,
replication_slot,
GetDbnameFromConnectionOptions());
/* /*
* Run IDENTIFY_SYSTEM so we can get the timeline * Run IDENTIFY_SYSTEM so we can get the timeline

View File

@ -34,6 +34,7 @@
int WalSegSz; int WalSegSz;
static bool RetrieveDataDirCreatePerm(PGconn *conn); static bool RetrieveDataDirCreatePerm(PGconn *conn);
static void FindDbnameInConnParams(PQconninfoOption *conn_opts, char **dbname);
/* SHOW command for replication connection was introduced in version 10 */ /* SHOW command for replication connection was introduced in version 10 */
#define MINIMUM_VERSION_FOR_SHOW_CMD 100000 #define MINIMUM_VERSION_FOR_SHOW_CMD 100000
@ -267,6 +268,75 @@ GetConnection(void)
return tmpconn; return tmpconn;
} }
/*
* FindDbnameInConnParams
*
* This is a helper function for GetDbnameFromConnectionOptions(). Extract
* the value of dbname from PQconninfoOption parameters.
*/
static void
FindDbnameInConnParams(PQconninfoOption *conn_opts, char **dbname)
{
PQconninfoOption *conn_opt;
Assert(dbname != NULL);
for (conn_opt = conn_opts; conn_opt->keyword != NULL; conn_opt++)
{
if ((strcmp(conn_opt->keyword, "dbname") == 0) &&
conn_opt->val != NULL && conn_opt->val[0] != '\0')
*dbname = pg_strdup(conn_opt->val);
}
}
/*
* GetDbnameFromConnectionOptions
*
* This is a special purpose function to retrieve the dbname from either the
* connection_string specified by the user or from the environment variables.
*
* We follow GetConnection() to fetch the dbname from various connection
* options.
*
* Returns NULL, if dbname is not specified by the user in the above
* mentioned connection options.
*/
char *
GetDbnameFromConnectionOptions(void)
{
PQconninfoOption *conn_opts = NULL;
char *err_msg = NULL;
char *dbname = NULL;
/* First try to get the dbname from connection string. */
if (connection_string)
{
conn_opts = PQconninfoParse(connection_string, &err_msg);
if (conn_opts == NULL)
pg_fatal("%s", err_msg);
FindDbnameInConnParams(conn_opts, &dbname);
if (dbname)
{
PQconninfoFree(conn_opts);
return dbname;
}
}
/*
* Next try to get the dbname from default values that are available from
* the environment.
*/
conn_opts = PQconndefaults();
if (conn_opts == NULL)
pg_fatal("out of memory");
FindDbnameInConnParams(conn_opts, &dbname);
PQconninfoFree(conn_opts);
return dbname;
}
/* /*
* From version 10, explicitly set wal segment size using SHOW wal_segment_size * From version 10, explicitly set wal segment size using SHOW wal_segment_size
* since ControlFile is not accessible here. * since ControlFile is not accessible here.

View File

@ -31,6 +31,8 @@ extern PGconn *conn;
extern PGconn *GetConnection(void); extern PGconn *GetConnection(void);
extern char *GetDbnameFromConnectionOptions(void);
/* Replication commands */ /* Replication commands */
extern bool CreateReplicationSlot(PGconn *conn, const char *slot_name, extern bool CreateReplicationSlot(PGconn *conn, const char *slot_name,
const char *plugin, bool is_temporary, const char *plugin, bool is_temporary,

View File

@ -783,6 +783,19 @@ my $checksum = $node->safe_psql('postgres', 'SHOW data_checksums;');
is($checksum, 'on', 'checksums are enabled'); is($checksum, 'on', 'checksums are enabled');
rmtree("$tempdir/backupxs_sl_R"); rmtree("$tempdir/backupxs_sl_R");
$node->command_ok(
[
@pg_basebackup_defs, '-D', "$tempdir/backup_dbname_R", '-X',
'stream', '-d', "dbname=db1", '-R',
],
'pg_basebackup with dbname and -R runs');
like(
slurp_file("$tempdir/backup_dbname_R/postgresql.auto.conf"),
qr/dbname=db1/m,
'recovery conf file sets dbname');
rmtree("$tempdir/backup_dbname_R");
# create tables to corrupt and get their relfilenodes # create tables to corrupt and get their relfilenodes
my $file_corrupt1 = $node->safe_psql('postgres', my $file_corrupt1 = $node->safe_psql('postgres',
q{CREATE TABLE corrupt1 AS SELECT a FROM generate_series(1,10000) AS a; ALTER TABLE corrupt1 SET (autovacuum_enabled=false); SELECT pg_relation_filepath('corrupt1')} q{CREATE TABLE corrupt1 AS SELECT a FROM generate_series(1,10000) AS a; ALTER TABLE corrupt1 SET (autovacuum_enabled=false); SELECT pg_relation_filepath('corrupt1')}

View File

@ -451,7 +451,7 @@ main(int argc, char **argv)
pg_log_info("no rewind required"); pg_log_info("no rewind required");
if (writerecoveryconf && !dry_run) if (writerecoveryconf && !dry_run)
WriteRecoveryConfig(conn, datadir_target, WriteRecoveryConfig(conn, datadir_target,
GenerateRecoveryConfig(conn, NULL)); GenerateRecoveryConfig(conn, NULL, NULL));
exit(0); exit(0);
} }
@ -525,7 +525,7 @@ main(int argc, char **argv)
/* Also update the standby configuration, if requested. */ /* Also update the standby configuration, if requested. */
if (writerecoveryconf && !dry_run) if (writerecoveryconf && !dry_run)
WriteRecoveryConfig(conn, datadir_target, WriteRecoveryConfig(conn, datadir_target,
GenerateRecoveryConfig(conn, NULL)); GenerateRecoveryConfig(conn, NULL, NULL));
/* don't need the source connection anymore */ /* don't need the source connection anymore */
source->destroy(source); source->destroy(source);

View File

@ -18,9 +18,14 @@ static char *escape_quotes(const char *src);
/* /*
* Write recovery configuration contents into a fresh PQExpBuffer, and * Write recovery configuration contents into a fresh PQExpBuffer, and
* return it. * return it.
*
* This accepts the dbname which will be appended to the primary_conninfo.
* The dbname will be ignored by walreciever process but slotsync worker uses
* it to connect to the primary server.
*/ */
PQExpBuffer PQExpBuffer
GenerateRecoveryConfig(PGconn *pgconn, const char *replication_slot) GenerateRecoveryConfig(PGconn *pgconn, const char *replication_slot,
char *dbname)
{ {
PQconninfoOption *connOptions; PQconninfoOption *connOptions;
PQExpBufferData conninfo_buf; PQExpBufferData conninfo_buf;
@ -66,6 +71,20 @@ GenerateRecoveryConfig(PGconn *pgconn, const char *replication_slot)
appendPQExpBuffer(&conninfo_buf, "%s=", opt->keyword); appendPQExpBuffer(&conninfo_buf, "%s=", opt->keyword);
appendConnStrVal(&conninfo_buf, opt->val); appendConnStrVal(&conninfo_buf, opt->val);
} }
if (dbname)
{
/*
* If dbname is specified in the connection, append the dbname. This
* will be used later for logical replication slot synchronization.
*/
if (conninfo_buf.len != 0)
appendPQExpBufferChar(&conninfo_buf, ' ');
appendPQExpBuffer(&conninfo_buf, "%s=", "dbname");
appendConnStrVal(&conninfo_buf, dbname);
}
if (PQExpBufferDataBroken(conninfo_buf)) if (PQExpBufferDataBroken(conninfo_buf))
pg_fatal("out of memory"); pg_fatal("out of memory");

View File

@ -21,7 +21,8 @@
#define MINIMUM_VERSION_FOR_RECOVERY_GUC 120000 #define MINIMUM_VERSION_FOR_RECOVERY_GUC 120000
extern PQExpBuffer GenerateRecoveryConfig(PGconn *pgconn, extern PQExpBuffer GenerateRecoveryConfig(PGconn *pgconn,
const char *replication_slot); const char *replication_slot,
char *dbname);
extern void WriteRecoveryConfig(PGconn *pgconn, const char *target_dir, extern void WriteRecoveryConfig(PGconn *pgconn, const char *target_dir,
PQExpBuffer contents); PQExpBuffer contents);