LCOV - code coverage report
Current view: top level - src/fe_utils - recovery_gen.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 0.0 % 107 0
Test Date: 2026-01-26 10:56:24 Functions: 0.0 % 5 0
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /*-------------------------------------------------------------------------
       2              :  *
       3              :  * recovery_gen.c
       4              :  *              Generator for recovery configuration
       5              :  *
       6              :  * Portions Copyright (c) 2011-2026, 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              : static 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              :  */
      27              : PQExpBuffer
      28            0 : GenerateRecoveryConfig(PGconn *pgconn, const char *replication_slot,
      29              :                                            char *dbname)
      30              : {
      31            0 :         PQconninfoOption *connOptions;
      32            0 :         PQExpBufferData conninfo_buf;
      33            0 :         char       *escaped;
      34            0 :         PQExpBuffer contents;
      35              : 
      36            0 :         Assert(pgconn != NULL);
      37              : 
      38            0 :         contents = createPQExpBuffer();
      39            0 :         if (!contents)
      40            0 :                 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              :          */
      46            0 :         if (PQserverVersion(pgconn) < MINIMUM_VERSION_FOR_RECOVERY_GUC)
      47            0 :                 appendPQExpBufferStr(contents, "standby_mode = 'on'\n");
      48              : 
      49            0 :         connOptions = PQconninfo(pgconn);
      50            0 :         if (connOptions == NULL)
      51            0 :                 pg_fatal("out of memory");
      52              : 
      53            0 :         initPQExpBuffer(&conninfo_buf);
      54            0 :         for (PQconninfoOption *opt = connOptions; opt && opt->keyword; opt++)
      55              :         {
      56              :                 /* Omit empty settings and those libpqwalreceiver overrides. */
      57            0 :                 if (strcmp(opt->keyword, "replication") == 0 ||
      58            0 :                         strcmp(opt->keyword, "dbname") == 0 ||
      59            0 :                         strcmp(opt->keyword, "fallback_application_name") == 0 ||
      60            0 :                         (opt->val == NULL) ||
      61            0 :                         (opt->val != NULL && opt->val[0] == '\0'))
      62            0 :                         continue;
      63              : 
      64              :                 /* Separate key-value pairs with spaces */
      65            0 :                 if (conninfo_buf.len != 0)
      66            0 :                         appendPQExpBufferChar(&conninfo_buf, ' ');
      67              : 
      68              :                 /*
      69              :                  * Write "keyword=value" pieces, the value string is escaped and/or
      70              :                  * quoted if necessary.
      71              :                  */
      72            0 :                 appendPQExpBuffer(&conninfo_buf, "%s=", opt->keyword);
      73            0 :                 appendConnStrVal(&conninfo_buf, opt->val);
      74            0 :         }
      75              : 
      76            0 :         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            0 :                 if (conninfo_buf.len != 0)
      83            0 :                         appendPQExpBufferChar(&conninfo_buf, ' ');
      84              : 
      85            0 :                 appendPQExpBuffer(&conninfo_buf, "%s=", "dbname");
      86            0 :                 appendConnStrVal(&conninfo_buf, dbname);
      87            0 :         }
      88              : 
      89            0 :         if (PQExpBufferDataBroken(conninfo_buf))
      90            0 :                 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            0 :         escaped = escape_quotes(conninfo_buf.data);
      98            0 :         termPQExpBuffer(&conninfo_buf);
      99            0 :         appendPQExpBuffer(contents, "primary_conninfo = '%s'\n", escaped);
     100            0 :         free(escaped);
     101              : 
     102            0 :         if (replication_slot)
     103              :         {
     104              :                 /* unescaped: ReplicationSlotValidateName allows [a-z0-9_] only */
     105            0 :                 appendPQExpBuffer(contents, "primary_slot_name = '%s'\n",
     106            0 :                                                   replication_slot);
     107            0 :         }
     108              : 
     109            0 :         if (PQExpBufferBroken(contents))
     110            0 :                 pg_fatal("out of memory");
     111              : 
     112            0 :         PQconninfoFree(connOptions);
     113              : 
     114            0 :         return contents;
     115            0 : }
     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              :  */
     124              : void
     125            0 : WriteRecoveryConfig(PGconn *pgconn, const char *target_dir, PQExpBuffer contents)
     126              : {
     127            0 :         char            filename[MAXPGPATH];
     128            0 :         FILE       *cf;
     129            0 :         bool            use_recovery_conf;
     130              : 
     131            0 :         Assert(pgconn != NULL);
     132              : 
     133            0 :         use_recovery_conf =
     134            0 :                 PQserverVersion(pgconn) < MINIMUM_VERSION_FOR_RECOVERY_GUC;
     135              : 
     136            0 :         snprintf(filename, MAXPGPATH, "%s/%s", target_dir,
     137            0 :                          use_recovery_conf ? "recovery.conf" : "postgresql.auto.conf");
     138              : 
     139            0 :         cf = fopen(filename, use_recovery_conf ? "w" : "a");
     140            0 :         if (cf == NULL)
     141            0 :                 pg_fatal("could not open file \"%s\": %m", filename);
     142              : 
     143            0 :         if (fwrite(contents->data, contents->len, 1, cf) != 1)
     144            0 :                 pg_fatal("could not write to file \"%s\": %m", filename);
     145              : 
     146            0 :         fclose(cf);
     147              : 
     148            0 :         if (!use_recovery_conf)
     149              :         {
     150            0 :                 snprintf(filename, MAXPGPATH, "%s/%s", target_dir, "standby.signal");
     151            0 :                 cf = fopen(filename, "w");
     152            0 :                 if (cf == NULL)
     153            0 :                         pg_fatal("could not create file \"%s\": %m", filename);
     154              : 
     155            0 :                 fclose(cf);
     156            0 :         }
     157            0 : }
     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              :  */
     163              : static char *
     164            0 : escape_quotes(const char *src)
     165              : {
     166            0 :         char       *result = escape_single_quotes_ascii(src);
     167              : 
     168            0 :         if (!result)
     169            0 :                 pg_fatal("out of memory");
     170            0 :         return result;
     171            0 : }
     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              :  */
     180              : static char *
     181            0 : FindDbnameInConnOpts(PQconninfoOption *conn_opts)
     182              : {
     183            0 :         for (PQconninfoOption *conn_opt = conn_opts;
     184            0 :                  conn_opt->keyword != NULL;
     185            0 :                  conn_opt++)
     186              :         {
     187            0 :                 if (strcmp(conn_opt->keyword, "dbname") == 0 &&
     188            0 :                         conn_opt->val != NULL && conn_opt->val[0] != '\0')
     189            0 :                         return pg_strdup(conn_opt->val);
     190            0 :         }
     191            0 :         return NULL;
     192            0 : }
     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              :  */
     203              : char *
     204            0 : GetDbnameFromConnectionOptions(const char *connstr)
     205              : {
     206            0 :         PQconninfoOption *conn_opts;
     207            0 :         char       *err_msg = NULL;
     208            0 :         char       *dbname;
     209              : 
     210              :         /* First try to get the dbname from connection string. */
     211            0 :         if (connstr)
     212              :         {
     213            0 :                 conn_opts = PQconninfoParse(connstr, &err_msg);
     214            0 :                 if (conn_opts == NULL)
     215            0 :                         pg_fatal("%s", err_msg);
     216              : 
     217            0 :                 dbname = FindDbnameInConnOpts(conn_opts);
     218              : 
     219            0 :                 PQconninfoFree(conn_opts);
     220            0 :                 if (dbname)
     221            0 :                         return dbname;
     222            0 :         }
     223              : 
     224              :         /*
     225              :          * Next try to get the dbname from default values that are available from
     226              :          * the environment.
     227              :          */
     228            0 :         conn_opts = PQconndefaults();
     229            0 :         if (conn_opts == NULL)
     230            0 :                 pg_fatal("out of memory");
     231              : 
     232            0 :         dbname = FindDbnameInConnOpts(conn_opts);
     233              : 
     234            0 :         PQconninfoFree(conn_opts);
     235            0 :         return dbname;
     236            0 : }
        

Generated by: LCOV version 2.3.2-1