LCOV - code coverage report
Current view: top level - src/fe_utils - recovery_gen.c (source / functions) Hit Total Coverage
Test: PostgreSQL 17devel Lines: 51 60 85.0 %
Date: 2024-04-25 20:11:01 Functions: 3 3 100.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-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             :  *
      22             :  * This accepts the dbname which will be appended to the primary_conninfo.
      23             :  * The dbname will be ignored by walreciever process but slotsync worker uses
      24             :  * it to connect to the primary server.
      25             :  */
      26             : PQExpBuffer
      27          22 : GenerateRecoveryConfig(PGconn *pgconn, const char *replication_slot,
      28             :                        char *dbname)
      29             : {
      30             :     PQconninfoOption *connOptions;
      31             :     PQExpBufferData conninfo_buf;
      32             :     char       *escaped;
      33             :     PQExpBuffer contents;
      34             : 
      35             :     Assert(pgconn != NULL);
      36             : 
      37          22 :     contents = createPQExpBuffer();
      38          22 :     if (!contents)
      39           0 :         pg_fatal("out of memory");
      40             : 
      41             :     /*
      42             :      * In PostgreSQL 12 and newer versions, standby_mode is gone, replaced by
      43             :      * standby.signal to trigger a standby state at recovery.
      44             :      */
      45          22 :     if (PQserverVersion(pgconn) < MINIMUM_VERSION_FOR_RECOVERY_GUC)
      46           0 :         appendPQExpBufferStr(contents, "standby_mode = 'on'\n");
      47             : 
      48          22 :     connOptions = PQconninfo(pgconn);
      49          22 :     if (connOptions == NULL)
      50           0 :         pg_fatal("out of memory");
      51             : 
      52          22 :     initPQExpBuffer(&conninfo_buf);
      53         924 :     for (PQconninfoOption *opt = connOptions; opt && opt->keyword; opt++)
      54             :     {
      55             :         /* Omit empty settings and those libpqwalreceiver overrides. */
      56         902 :         if (strcmp(opt->keyword, "replication") == 0 ||
      57         880 :             strcmp(opt->keyword, "dbname") == 0 ||
      58         858 :             strcmp(opt->keyword, "fallback_application_name") == 0 ||
      59         836 :             (opt->val == NULL) ||
      60         394 :             (opt->val != NULL && opt->val[0] == '\0'))
      61         530 :             continue;
      62             : 
      63             :         /* Separate key-value pairs with spaces */
      64         372 :         if (conninfo_buf.len != 0)
      65         350 :             appendPQExpBufferChar(&conninfo_buf, ' ');
      66             : 
      67             :         /*
      68             :          * Write "keyword=value" pieces, the value string is escaped and/or
      69             :          * quoted if necessary.
      70             :          */
      71         372 :         appendPQExpBuffer(&conninfo_buf, "%s=", opt->keyword);
      72         372 :         appendConnStrVal(&conninfo_buf, opt->val);
      73             :     }
      74             : 
      75          22 :     if (dbname)
      76             :     {
      77             :         /*
      78             :          * If dbname is specified in the connection, append the dbname. This
      79             :          * will be used later for logical replication slot synchronization.
      80             :          */
      81           6 :         if (conninfo_buf.len != 0)
      82           6 :             appendPQExpBufferChar(&conninfo_buf, ' ');
      83             : 
      84           6 :         appendPQExpBuffer(&conninfo_buf, "%s=", "dbname");
      85           6 :         appendConnStrVal(&conninfo_buf, dbname);
      86             :     }
      87             : 
      88          22 :     if (PQExpBufferDataBroken(conninfo_buf))
      89           0 :         pg_fatal("out of memory");
      90             : 
      91             :     /*
      92             :      * Escape the connection string, so that it can be put in the config file.
      93             :      * Note that this is different from the escaping of individual connection
      94             :      * options above!
      95             :      */
      96          22 :     escaped = escape_quotes(conninfo_buf.data);
      97          22 :     termPQExpBuffer(&conninfo_buf);
      98          22 :     appendPQExpBuffer(contents, "primary_conninfo = '%s'\n", escaped);
      99          22 :     free(escaped);
     100             : 
     101          22 :     if (replication_slot)
     102             :     {
     103             :         /* unescaped: ReplicationSlotValidateName allows [a-z0-9_] only */
     104           2 :         appendPQExpBuffer(contents, "primary_slot_name = '%s'\n",
     105             :                           replication_slot);
     106             :     }
     107             : 
     108          22 :     if (PQExpBufferBroken(contents))
     109           0 :         pg_fatal("out of memory");
     110             : 
     111          22 :     PQconninfoFree(connOptions);
     112             : 
     113          22 :     return contents;
     114             : }
     115             : 
     116             : /*
     117             :  * Write the configuration file in the directory specified in target_dir,
     118             :  * with the contents already collected in memory appended.  Then write
     119             :  * the signal file into the target_dir.  If the server does not support
     120             :  * recovery parameters as GUCs, the signal file is not necessary, and
     121             :  * configuration is written to recovery.conf.
     122             :  */
     123             : void
     124          12 : WriteRecoveryConfig(PGconn *pgconn, const char *target_dir, PQExpBuffer contents)
     125             : {
     126             :     char        filename[MAXPGPATH];
     127             :     FILE       *cf;
     128             :     bool        use_recovery_conf;
     129             : 
     130             :     Assert(pgconn != NULL);
     131             : 
     132          12 :     use_recovery_conf =
     133          12 :         PQserverVersion(pgconn) < MINIMUM_VERSION_FOR_RECOVERY_GUC;
     134             : 
     135          12 :     snprintf(filename, MAXPGPATH, "%s/%s", target_dir,
     136             :              use_recovery_conf ? "recovery.conf" : "postgresql.auto.conf");
     137             : 
     138          12 :     cf = fopen(filename, use_recovery_conf ? "w" : "a");
     139          12 :     if (cf == NULL)
     140           0 :         pg_fatal("could not open file \"%s\": %m", filename);
     141             : 
     142          12 :     if (fwrite(contents->data, contents->len, 1, cf) != 1)
     143           0 :         pg_fatal("could not write to file \"%s\": %m", filename);
     144             : 
     145          12 :     fclose(cf);
     146             : 
     147          12 :     if (!use_recovery_conf)
     148             :     {
     149          12 :         snprintf(filename, MAXPGPATH, "%s/%s", target_dir, "standby.signal");
     150          12 :         cf = fopen(filename, "w");
     151          12 :         if (cf == NULL)
     152           0 :             pg_fatal("could not create file \"%s\": %m", filename);
     153             : 
     154          12 :         fclose(cf);
     155             :     }
     156          12 : }
     157             : 
     158             : /*
     159             :  * Escape a string so that it can be used as a value in a key-value pair
     160             :  * a configuration file.
     161             :  */
     162             : static char *
     163          22 : escape_quotes(const char *src)
     164             : {
     165          22 :     char       *result = escape_single_quotes_ascii(src);
     166             : 
     167          22 :     if (!result)
     168           0 :         pg_fatal("out of memory");
     169          22 :     return result;
     170             : }

Generated by: LCOV version 1.14