/*-------------------------------------------------------------------------
*
* recovery_gen . c
* Generator for recovery configuration
*
* Portions Copyright ( c ) 2011 - 2025 , PostgreSQL Global Development Group
*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
#include "postgres_fe.h"
#include "common/logging.h"
#include "fe_utils/recovery_gen.h"
#include "fe_utils/string_utils.h"
static char *escape_quotes(const char *src);
static char *FindDbnameInConnOpts(PQconninfoOption *conn_opts);
/*
* Write recovery configuration contents into a fresh PQExpBuffer , and
* return it .
*
* This accepts the dbname which will be appended to the primary_conninfo .
* The dbname will be ignored by walreceiver process but slotsync worker uses
* it to connect to the primary server .
*/
PQExpBuffer
GenerateRecoveryConfig(PGconn *pgconn, const char *replication_slot,
char *dbname)
{
PQconninfoOption *connOptions;
PQExpBufferData conninfo_buf;
char *escaped;
PQExpBuffer contents;
Assert(pgconn != NULL);
contents = createPQExpBuffer();
if (!contents)
pg_fatal("out of memory" );
/*
* In PostgreSQL 12 and newer versions , standby_mode is gone , replaced by
* standby . signal to trigger a standby state at recovery .
*/
if (PQserverVersion(pgconn) < MINIMUM_VERSION_FOR_RECOVERY_GUC)
appendPQExpBufferStr(contents, "standby_mode = 'on'\n" );
connOptions = PQconninfo(pgconn);
if (connOptions == NULL)
pg_fatal("out of memory" );
initPQExpBuffer(&conninfo_buf);
for (PQconninfoOption *opt = connOptions; opt && opt->keyword; opt++)
{
/* Omit empty settings and those libpqwalreceiver overrides. */
if (strcmp(opt->keyword, "replication" ) == 0 ||
strcmp(opt->keyword, "dbname" ) == 0 ||
strcmp(opt->keyword, "fallback_application_name" ) == 0 ||
(opt->val == NULL) ||
(opt->val != NULL && opt->val[0 ] == '\0' ))
continue ;
/* Separate key-value pairs with spaces */
if (conninfo_buf.len != 0 )
appendPQExpBufferChar(&conninfo_buf, ' ' );
/*
* Write " keyword = value " pieces , the value string is escaped and / or
* quoted if necessary .
*/
appendPQExpBuffer(&conninfo_buf, "%s=" , opt->keyword);
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))
pg_fatal("out of memory" );
/*
* Escape the connection string , so that it can be put in the config file .
* Note that this is different from the escaping of individual connection
* options above !
*/
escaped = escape_quotes(conninfo_buf.data);
termPQExpBuffer(&conninfo_buf);
appendPQExpBuffer(contents, "primary_conninfo = '%s'\n" , escaped);
free(escaped);
if (replication_slot)
{
/* unescaped: ReplicationSlotValidateName allows [a-z0-9_] only */
appendPQExpBuffer(contents, "primary_slot_name = '%s'\n" ,
replication_slot);
}
if (PQExpBufferBroken(contents))
pg_fatal("out of memory" );
PQconninfoFree(connOptions);
return contents;
}
/*
* Write the configuration file in the directory specified in target_dir ,
* with the contents already collected in memory appended . Then write
* the signal file into the target_dir . If the server does not support
* recovery parameters as GUCs , the signal file is not necessary , and
* configuration is written to recovery . conf .
*/
void
WriteRecoveryConfig(PGconn *pgconn, const char *target_dir, PQExpBuffer contents)
{
char filename[MAXPGPATH];
FILE *cf;
bool use_recovery_conf;
Assert(pgconn != NULL);
use_recovery_conf =
PQserverVersion(pgconn) < MINIMUM_VERSION_FOR_RECOVERY_GUC;
snprintf(filename, MAXPGPATH, "%s/%s" , target_dir,
use_recovery_conf ? "recovery.conf" : "postgresql.auto.conf" );
cf = fopen(filename, use_recovery_conf ? "w" : "a" );
if (cf == NULL)
pg_fatal("could not open file \" %s\": %m" , filename);
if (fwrite(contents->data, contents->len, 1 , cf) != 1 )
pg_fatal("could not write to file \" %s\": %m" , filename);
fclose(cf);
if (!use_recovery_conf)
{
snprintf(filename, MAXPGPATH, "%s/%s" , target_dir, "standby.signal" );
cf = fopen(filename, "w" );
if (cf == NULL)
pg_fatal("could not create file \" %s\": %m" , filename);
fclose(cf);
}
}
/*
* Escape a string so that it can be used as a value in a key - value pair
* a configuration file .
*/
static char *
escape_quotes(const char *src)
{
char *result = escape_single_quotes_ascii(src);
if (!result)
pg_fatal("out of memory" );
return result;
}
/*
* FindDbnameInConnOpts
*
* This is a helper function for GetDbnameFromConnectionOptions ( ) . Extract
* the value of dbname from PQconninfoOption parameters , if it ' s present .
* Returns a strdup ' d result or NULL .
*/
static char *
FindDbnameInConnOpts(PQconninfoOption *conn_opts)
{
for (PQconninfoOption *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' )
return pg_strdup(conn_opt->val);
}
return NULL;
}
/*
* GetDbnameFromConnectionOptions
*
* This is a special purpose function to retrieve the dbname from either the
* ' connstr ' specified by the caller or from the environment variables .
*
* Returns NULL , if dbname is not specified by the user in the given
* connection options .
*/
char *
GetDbnameFromConnectionOptions(const char *connstr)
{
PQconninfoOption *conn_opts;
char *err_msg = NULL;
char *dbname;
/* First try to get the dbname from connection string. */
if (connstr)
{
conn_opts = PQconninfoParse(connstr, &err_msg);
if (conn_opts == NULL)
pg_fatal("%s" , err_msg);
dbname = FindDbnameInConnOpts(conn_opts);
PQconninfoFree(conn_opts);
if (dbname)
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" );
dbname = FindDbnameInConnOpts(conn_opts);
PQconninfoFree(conn_opts);
return dbname;
}
Messung V0.5 in Prozent C=97 H=94 G=95
¤ Dauer der Verarbeitung: 0.14 Sekunden
(vorverarbeitet am 2026-08-08)
¤
*© Formatika GbR, Deutschland