PostgreSQL Source Code git master
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
recovery_gen.c
Go to the documentation of this file.
1/*-------------------------------------------------------------------------
2 *
3 * recovery_gen.c
4 * Generator for recovery configuration
5 *
6 * Portions Copyright (c) 2011-2025, PostgreSQL Global Development Group
7 *
8 *-------------------------------------------------------------------------
9 */
10#include "postgres_fe.h"
11
12#include "common/logging.h"
15
16static char *escape_quotes(const char *src);
17static char *FindDbnameInConnOpts(PQconninfoOption *conn_opts);
18
19/*
20 * Write recovery configuration contents into a fresh PQExpBuffer, and
21 * return it.
22 *
23 * This accepts the dbname which will be appended to the primary_conninfo.
24 * The dbname will be ignored by walreceiver process but slotsync worker uses
25 * it to connect to the primary server.
26 */
29 char *dbname)
30{
31 PQconninfoOption *connOptions;
32 PQExpBufferData conninfo_buf;
33 char *escaped;
34 PQExpBuffer contents;
35
36 Assert(pgconn != NULL);
37
38 contents = createPQExpBuffer();
39 if (!contents)
40 pg_fatal("out of memory");
41
42 /*
43 * In PostgreSQL 12 and newer versions, standby_mode is gone, replaced by
44 * standby.signal to trigger a standby state at recovery.
45 */
47 appendPQExpBufferStr(contents, "standby_mode = 'on'\n");
48
49 connOptions = PQconninfo(pgconn);
50 if (connOptions == NULL)
51 pg_fatal("out of memory");
52
53 initPQExpBuffer(&conninfo_buf);
54 for (PQconninfoOption *opt = connOptions; opt && opt->keyword; opt++)
55 {
56 /* Omit empty settings and those libpqwalreceiver overrides. */
57 if (strcmp(opt->keyword, "replication") == 0 ||
58 strcmp(opt->keyword, "dbname") == 0 ||
59 strcmp(opt->keyword, "fallback_application_name") == 0 ||
60 (opt->val == NULL) ||
61 (opt->val != NULL && opt->val[0] == '\0'))
62 continue;
63
64 /* Separate key-value pairs with spaces */
65 if (conninfo_buf.len != 0)
66 appendPQExpBufferChar(&conninfo_buf, ' ');
67
68 /*
69 * Write "keyword=value" pieces, the value string is escaped and/or
70 * quoted if necessary.
71 */
72 appendPQExpBuffer(&conninfo_buf, "%s=", opt->keyword);
73 appendConnStrVal(&conninfo_buf, opt->val);
74 }
75
76 if (dbname)
77 {
78 /*
79 * If dbname is specified in the connection, append the dbname. This
80 * will be used later for logical replication slot synchronization.
81 */
82 if (conninfo_buf.len != 0)
83 appendPQExpBufferChar(&conninfo_buf, ' ');
84
85 appendPQExpBuffer(&conninfo_buf, "%s=", "dbname");
86 appendConnStrVal(&conninfo_buf, dbname);
87 }
88
89 if (PQExpBufferDataBroken(conninfo_buf))
90 pg_fatal("out of memory");
91
92 /*
93 * Escape the connection string, so that it can be put in the config file.
94 * Note that this is different from the escaping of individual connection
95 * options above!
96 */
97 escaped = escape_quotes(conninfo_buf.data);
98 termPQExpBuffer(&conninfo_buf);
99 appendPQExpBuffer(contents, "primary_conninfo = '%s'\n", escaped);
100 free(escaped);
101
103 {
104 /* unescaped: ReplicationSlotValidateName allows [a-z0-9_] only */
105 appendPQExpBuffer(contents, "primary_slot_name = '%s'\n",
107 }
108
109 if (PQExpBufferBroken(contents))
110 pg_fatal("out of memory");
111
112 PQconninfoFree(connOptions);
113
114 return contents;
115}
116
117/*
118 * Write the configuration file in the directory specified in target_dir,
119 * with the contents already collected in memory appended. Then write
120 * the signal file into the target_dir. If the server does not support
121 * recovery parameters as GUCs, the signal file is not necessary, and
122 * configuration is written to recovery.conf.
123 */
124void
125WriteRecoveryConfig(PGconn *pgconn, const char *target_dir, PQExpBuffer contents)
126{
127 char filename[MAXPGPATH];
128 FILE *cf;
129 bool use_recovery_conf;
130
131 Assert(pgconn != NULL);
132
133 use_recovery_conf =
135
136 snprintf(filename, MAXPGPATH, "%s/%s", target_dir,
137 use_recovery_conf ? "recovery.conf" : "postgresql.auto.conf");
138
139 cf = fopen(filename, use_recovery_conf ? "w" : "a");
140 if (cf == NULL)
141 pg_fatal("could not open file \"%s\": %m", filename);
142
143 if (fwrite(contents->data, contents->len, 1, cf) != 1)
144 pg_fatal("could not write to file \"%s\": %m", filename);
145
146 fclose(cf);
147
148 if (!use_recovery_conf)
149 {
150 snprintf(filename, MAXPGPATH, "%s/%s", target_dir, "standby.signal");
151 cf = fopen(filename, "w");
152 if (cf == NULL)
153 pg_fatal("could not create file \"%s\": %m", filename);
154
155 fclose(cf);
156 }
157}
158
159/*
160 * Escape a string so that it can be used as a value in a key-value pair
161 * a configuration file.
162 */
163static char *
164escape_quotes(const char *src)
165{
166 char *result = escape_single_quotes_ascii(src);
167
168 if (!result)
169 pg_fatal("out of memory");
170 return result;
171}
172
173/*
174 * FindDbnameInConnOpts
175 *
176 * This is a helper function for GetDbnameFromConnectionOptions(). Extract
177 * the value of dbname from PQconninfoOption parameters, if it's present.
178 * Returns a strdup'd result or NULL.
179 */
180static char *
182{
183 for (PQconninfoOption *conn_opt = conn_opts;
184 conn_opt->keyword != NULL;
185 conn_opt++)
186 {
187 if (strcmp(conn_opt->keyword, "dbname") == 0 &&
188 conn_opt->val != NULL && conn_opt->val[0] != '\0')
189 return pg_strdup(conn_opt->val);
190 }
191 return NULL;
192}
193
194/*
195 * GetDbnameFromConnectionOptions
196 *
197 * This is a special purpose function to retrieve the dbname from either the
198 * 'connstr' specified by the caller or from the environment variables.
199 *
200 * Returns NULL, if dbname is not specified by the user in the given
201 * connection options.
202 */
203char *
205{
206 PQconninfoOption *conn_opts;
207 char *err_msg = NULL;
208 char *dbname;
209
210 /* First try to get the dbname from connection string. */
211 if (connstr)
212 {
213 conn_opts = PQconninfoParse(connstr, &err_msg);
214 if (conn_opts == NULL)
215 pg_fatal("%s", err_msg);
216
217 dbname = FindDbnameInConnOpts(conn_opts);
218
219 PQconninfoFree(conn_opts);
220 if (dbname)
221 return dbname;
222 }
223
224 /*
225 * Next try to get the dbname from default values that are available from
226 * the environment.
227 */
228 conn_opts = PQconndefaults();
229 if (conn_opts == NULL)
230 pg_fatal("out of memory");
231
232 dbname = FindDbnameInConnOpts(conn_opts);
233
234 PQconninfoFree(conn_opts);
235 return dbname;
236}
int PQserverVersion(const PGconn *conn)
Definition: fe-connect.c:7609
PQconninfoOption * PQconninfo(PGconn *conn)
Definition: fe-connect.c:7390
void PQconninfoFree(PQconninfoOption *connOptions)
Definition: fe-connect.c:7434
PQconninfoOption * PQconninfoParse(const char *conninfo, char **errmsg)
Definition: fe-connect.c:6150
PQconninfoOption * PQconndefaults(void)
Definition: fe-connect.c:2190
char * pg_strdup(const char *in)
Definition: fe_memutils.c:85
Assert(PointerIsAligned(start, uint64))
#define free(a)
Definition: header.h:65
#define pg_fatal(...)
static char * replication_slot
#define MAXPGPATH
static const char * connstr
Definition: pg_dumpall.c:84
static char * filename
Definition: pg_dumpall.c:123
char * escape_single_quotes_ascii(const char *src)
Definition: quotes.c:33
#define snprintf
Definition: port.h:239
PQExpBuffer createPQExpBuffer(void)
Definition: pqexpbuffer.c:72
void initPQExpBuffer(PQExpBuffer str)
Definition: pqexpbuffer.c:90
void appendPQExpBuffer(PQExpBuffer str, const char *fmt,...)
Definition: pqexpbuffer.c:265
void appendPQExpBufferChar(PQExpBuffer str, char ch)
Definition: pqexpbuffer.c:378
void appendPQExpBufferStr(PQExpBuffer str, const char *data)
Definition: pqexpbuffer.c:367
void termPQExpBuffer(PQExpBuffer str)
Definition: pqexpbuffer.c:129
#define PQExpBufferBroken(str)
Definition: pqexpbuffer.h:59
#define PQExpBufferDataBroken(buf)
Definition: pqexpbuffer.h:67
void WriteRecoveryConfig(PGconn *pgconn, const char *target_dir, PQExpBuffer contents)
Definition: recovery_gen.c:125
PQExpBuffer GenerateRecoveryConfig(PGconn *pgconn, const char *replication_slot, char *dbname)
Definition: recovery_gen.c:28
static char * FindDbnameInConnOpts(PQconninfoOption *conn_opts)
Definition: recovery_gen.c:181
char * GetDbnameFromConnectionOptions(const char *connstr)
Definition: recovery_gen.c:204
static char * escape_quotes(const char *src)
Definition: recovery_gen.c:164
#define MINIMUM_VERSION_FOR_RECOVERY_GUC
Definition: recovery_gen.h:21
char * dbname
Definition: streamutil.c:49
void appendConnStrVal(PQExpBuffer buf, const char *str)
Definition: string_utils.c:698