PostgreSQL Source Code  git master
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-2024, PostgreSQL Global Development Group
7  *
8  *-------------------------------------------------------------------------
9  */
10 #include "postgres_fe.h"
11 
12 #include "common/logging.h"
13 #include "fe_utils/recovery_gen.h"
14 #include "fe_utils/string_utils.h"
15 
16 static char *escape_quotes(const char *src);
17 
18 /*
19  * Write recovery configuration contents into a fresh PQExpBuffer, and
20  * return it.
21  */
24 {
25  PQconninfoOption *connOptions;
26  PQExpBufferData conninfo_buf;
27  char *escaped;
28  PQExpBuffer contents;
29 
30  Assert(pgconn != NULL);
31 
32  contents = createPQExpBuffer();
33  if (!contents)
34  pg_fatal("out of memory");
35 
36  /*
37  * In PostgreSQL 12 and newer versions, standby_mode is gone, replaced by
38  * standby.signal to trigger a standby state at recovery.
39  */
41  appendPQExpBufferStr(contents, "standby_mode = 'on'\n");
42 
43  connOptions = PQconninfo(pgconn);
44  if (connOptions == NULL)
45  pg_fatal("out of memory");
46 
47  initPQExpBuffer(&conninfo_buf);
48  for (PQconninfoOption *opt = connOptions; opt && opt->keyword; opt++)
49  {
50  /* Omit empty settings and those libpqwalreceiver overrides. */
51  if (strcmp(opt->keyword, "replication") == 0 ||
52  strcmp(opt->keyword, "dbname") == 0 ||
53  strcmp(opt->keyword, "fallback_application_name") == 0 ||
54  (opt->val == NULL) ||
55  (opt->val != NULL && opt->val[0] == '\0'))
56  continue;
57 
58  /* Separate key-value pairs with spaces */
59  if (conninfo_buf.len != 0)
60  appendPQExpBufferChar(&conninfo_buf, ' ');
61 
62  /*
63  * Write "keyword=value" pieces, the value string is escaped and/or
64  * quoted if necessary.
65  */
66  appendPQExpBuffer(&conninfo_buf, "%s=", opt->keyword);
67  appendConnStrVal(&conninfo_buf, opt->val);
68  }
69  if (PQExpBufferDataBroken(conninfo_buf))
70  pg_fatal("out of memory");
71 
72  /*
73  * Escape the connection string, so that it can be put in the config file.
74  * Note that this is different from the escaping of individual connection
75  * options above!
76  */
77  escaped = escape_quotes(conninfo_buf.data);
78  termPQExpBuffer(&conninfo_buf);
79  appendPQExpBuffer(contents, "primary_conninfo = '%s'\n", escaped);
80  free(escaped);
81 
82  if (replication_slot)
83  {
84  /* unescaped: ReplicationSlotValidateName allows [a-z0-9_] only */
85  appendPQExpBuffer(contents, "primary_slot_name = '%s'\n",
87  }
88 
89  if (PQExpBufferBroken(contents))
90  pg_fatal("out of memory");
91 
92  PQconninfoFree(connOptions);
93 
94  return contents;
95 }
96 
97 /*
98  * Write the configuration file in the directory specified in target_dir,
99  * with the contents already collected in memory appended. Then write
100  * the signal file into the target_dir. If the server does not support
101  * recovery parameters as GUCs, the signal file is not necessary, and
102  * configuration is written to recovery.conf.
103  */
104 void
105 WriteRecoveryConfig(PGconn *pgconn, const char *target_dir, PQExpBuffer contents)
106 {
107  char filename[MAXPGPATH];
108  FILE *cf;
109  bool use_recovery_conf;
110 
111  Assert(pgconn != NULL);
112 
113  use_recovery_conf =
115 
116  snprintf(filename, MAXPGPATH, "%s/%s", target_dir,
117  use_recovery_conf ? "recovery.conf" : "postgresql.auto.conf");
118 
119  cf = fopen(filename, use_recovery_conf ? "w" : "a");
120  if (cf == NULL)
121  pg_fatal("could not open file \"%s\": %m", filename);
122 
123  if (fwrite(contents->data, contents->len, 1, cf) != 1)
124  pg_fatal("could not write to file \"%s\": %m", filename);
125 
126  fclose(cf);
127 
128  if (!use_recovery_conf)
129  {
130  snprintf(filename, MAXPGPATH, "%s/%s", target_dir, "standby.signal");
131  cf = fopen(filename, "w");
132  if (cf == NULL)
133  pg_fatal("could not create file \"%s\": %m", filename);
134 
135  fclose(cf);
136  }
137 }
138 
139 /*
140  * Escape a string so that it can be used as a value in a key-value pair
141  * a configuration file.
142  */
143 static char *
144 escape_quotes(const char *src)
145 {
146  char *result = escape_single_quotes_ascii(src);
147 
148  if (!result)
149  pg_fatal("out of memory");
150  return result;
151 }
int PQserverVersion(const PGconn *conn)
Definition: fe-connect.c:6938
void PQconninfoFree(PQconninfoOption *connOptions)
Definition: fe-connect.c:6781
PQconninfoOption * PQconninfo(PGconn *conn)
Definition: fe-connect.c:6737
#define free(a)
Definition: header.h:65
Assert(fmt[strlen(fmt) - 1] !='\n')
#define pg_fatal(...)
static char * replication_slot
#define MAXPGPATH
static char * filename
Definition: pg_dumpall.c:121
#define snprintf
Definition: port.h:238
char * escape_single_quotes_ascii(const char *src)
Definition: quotes.c:33
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:105
PQExpBuffer GenerateRecoveryConfig(PGconn *pgconn, const char *replication_slot)
Definition: recovery_gen.c:23
static char * escape_quotes(const char *src)
Definition: recovery_gen.c:144
#define MINIMUM_VERSION_FOR_RECOVERY_GUC
Definition: recovery_gen.h:21
void appendConnStrVal(PQExpBuffer buf, const char *str)
Definition: string_utils.c:545