Re: Set arbitrary GUC options during initdb

Поиск
Список
Период
Сортировка
От Tom Lane
Тема Re: Set arbitrary GUC options during initdb
Дата
Msg-id 3527270.1674849724@sss.pgh.pa.us
обсуждение исходный текст
Ответ на Re: Set arbitrary GUC options during initdb  (Tom Lane <tgl@sss.pgh.pa.us>)
Ответы Re: Set arbitrary GUC options during initdb  (Robert Haas <robertmhaas@gmail.com>)
Re: Set arbitrary GUC options during initdb  (Peter Eisentraut <peter.eisentraut@enterprisedb.com>)
Список pgsql-hackers
I wrote:
>>> Anyway, it seems like I gotta work harder.  I'll produce a
>>> new patch.

The string-hacking was fully as tedious as I expected.  However, the
output looks pretty nice, and this does have the advantage that the
pre-programmed substitutions become a lot more robust: they are no
longer dependent on the initdb code exactly matching what is in
postgresql.conf.sample.

            regards, tom lane

diff --git a/doc/src/sgml/ref/initdb.sgml b/doc/src/sgml/ref/initdb.sgml
index 5b2bdac101..724188b1b5 100644
--- a/doc/src/sgml/ref/initdb.sgml
+++ b/doc/src/sgml/ref/initdb.sgml
@@ -433,6 +433,23 @@ PostgreSQL documentation
     Other, less commonly used, options are also available:

     <variablelist>
+     <varlistentry id="app-initdb-option-set">
+      <term><option>-c <replaceable>name</replaceable>=<replaceable>value</replaceable></option></term>
+      <term><option>--set <replaceable>name</replaceable>=<replaceable>value</replaceable></option></term>
+      <listitem>
+       <para>
+        Forcibly set the server parameter <replaceable>name</replaceable>
+        to <replaceable>value</replaceable> during <command>initdb</command>,
+        and also install that setting in the
+        generated <filename>postgresql.conf</filename> file,
+        so that it will apply during future server runs.
+        This option can be given more than once to set several parameters.
+        It is primarily useful when the environment is such that the server
+        will not start at all using the default parameters.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry id="app-initdb-option-debug">
       <term><option>-d</option></term>
       <term><option>--debug</option></term>
diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c
index 7a58c33ace..c955c0837e 100644
--- a/src/bin/initdb/initdb.c
+++ b/src/bin/initdb/initdb.c
@@ -48,6 +48,7 @@

 #include "postgres_fe.h"

+#include <ctype.h>
 #include <dirent.h>
 #include <fcntl.h>
 #include <netdb.h>
@@ -82,6 +83,13 @@
 /* Ideally this would be in a .h file, but it hardly seems worth the trouble */
 extern const char *select_default_timezone(const char *share_path);

+/* simple list of strings */
+typedef struct _stringlist
+{
+    char       *str;
+    struct _stringlist *next;
+} _stringlist;
+
 static const char *const auth_methods_host[] = {
     "trust", "reject", "scram-sha-256", "md5", "password", "ident", "radius",
 #ifdef ENABLE_GSS
@@ -142,6 +150,8 @@ static char *pwfilename = NULL;
 static char *superuser_password = NULL;
 static const char *authmethodhost = NULL;
 static const char *authmethodlocal = NULL;
+static _stringlist *extra_guc_names = NULL;
+static _stringlist *extra_guc_values = NULL;
 static bool debug = false;
 static bool noclean = false;
 static bool noinstructions = false;
@@ -242,7 +252,10 @@ static char backend_exec[MAXPGPATH];

 static char **replace_token(char **lines,
                             const char *token, const char *replacement);
-
+static char **replace_guc_value(char **lines,
+                                const char *guc_name, const char *guc_value,
+                                bool mark_as_comment);
+static bool guc_value_requires_quotes(const char *guc_value);
 static char **readfile(const char *path);
 static void writefile(char *path, char **lines);
 static FILE *popen_check(const char *command, const char *mode);
@@ -253,6 +266,7 @@ static void check_input(char *path);
 static void write_version_file(const char *extrapath);
 static void set_null_conf(void);
 static void test_config_settings(void);
+static bool test_specific_config_settings(int test_conns, int test_buffs);
 static void setup_config(void);
 static void bootstrap_template1(void);
 static void setup_auth(FILE *cmdfd);
@@ -360,9 +374,34 @@ escape_quotes_bki(const char *src)
 }

 /*
- * make a copy of the array of lines, with token replaced by replacement
+ * Add an item at the end of a stringlist.
+ */
+static void
+add_stringlist_item(_stringlist **listhead, const char *str)
+{
+    _stringlist *newentry = pg_malloc(sizeof(_stringlist));
+    _stringlist *oldentry;
+
+    newentry->str = pg_strdup(str);
+    newentry->next = NULL;
+    if (*listhead == NULL)
+        *listhead = newentry;
+    else
+    {
+        for (oldentry = *listhead; oldentry->next; oldentry = oldentry->next)
+             /* skip */ ;
+        oldentry->next = newentry;
+    }
+}
+
+/*
+ * Make a copy of the array of lines, with token replaced by replacement
  * the first time it occurs on each line.
  *
+ * The original data structure is not changed, but we share any unchanged
+ * strings with it.  (This definition lends itself to memory leaks, but
+ * we don't care too much about leaks in this program.)
+ *
  * This does most of what sed was used for in the shell script, but
  * doesn't need any regexp stuff.
  */
@@ -416,6 +455,168 @@ replace_token(char **lines, const char *token, const char *replacement)
     return result;
 }

+/*
+ * Make a copy of the array of lines, replacing the possibly-commented-out
+ * assignment of parameter guc_name with a live assignment of guc_value.
+ * The value will be suitably quoted.
+ *
+ * If mark_as_comment is true, the replacement line is prefixed with '#'.
+ * This is used for fixing up cases where the effective default might not
+ * match what is in postgresql.conf.sample.
+ *
+ * We assume there's at most one matching assignment.  If we find no match,
+ * append a new line with the desired assignment.
+ *
+ * The original data structure is not changed, but we share any unchanged
+ * strings with it.  (This definition lends itself to memory leaks, but
+ * we don't care too much about leaks in this program.)
+ */
+static char **
+replace_guc_value(char **lines, const char *guc_name, const char *guc_value,
+                  bool mark_as_comment)
+{
+    char      **result;
+    int            namelen = strlen(guc_name);
+    PQExpBuffer newline = createPQExpBuffer();
+    int            numlines = 0;
+    int            i;
+
+    /* prepare the replacement line, except for possible comment and newline */
+    if (mark_as_comment)
+        appendPQExpBufferChar(newline, '#');
+    appendPQExpBuffer(newline, "%s = ", guc_name);
+    if (guc_value_requires_quotes(guc_value))
+        appendPQExpBuffer(newline, "'%s'", escape_quotes(guc_value));
+    else
+        appendPQExpBufferStr(newline, guc_value);
+
+    /* create the new pointer array */
+    for (i = 0; lines[i]; i++)
+        numlines++;
+
+    /* leave room for one extra string in case we need to append */
+    result = (char **) pg_malloc((numlines + 2) * sizeof(char *));
+
+    /* initialize result with all the same strings */
+    memcpy(result, lines, (numlines + 1) * sizeof(char *));
+
+    for (i = 0; i < numlines; i++)
+    {
+        const char *where;
+
+        /*
+         * Look for a line assigning to guc_name.  Typically it will be
+         * preceded by '#', but that might not be the case if a -c switch
+         * overrides a previous assignment.  We allow leading whitespace too,
+         * although normally there wouldn't be any.
+         */
+        where = result[i];
+        while (*where == '#' || isspace((unsigned char) *where))
+            where++;
+        if (strncmp(where, guc_name, namelen) != 0)
+            continue;
+        where += namelen;
+        while (isspace((unsigned char) *where))
+            where++;
+        if (*where != '=')
+            continue;
+
+        /* found it -- append the original comment if any */
+        where = strrchr(where, '#');
+        if (where)
+        {
+            /*
+             * We try to preserve original indentation, which is tedious.
+             * oldindent and newindent are measured in de-tab-ified columns.
+             */
+            const char *ptr;
+            int            oldindent = 0;
+            int            newindent;
+
+            for (ptr = result[i]; ptr < where; ptr++)
+            {
+                if (*ptr == '\t')
+                    oldindent += 8 - (oldindent % 8);
+                else
+                    oldindent++;
+            }
+            /* ignore the possibility of tabs in guc_value */
+            newindent = newline->len;
+            /* append appropriate tabs and spaces, forcing at least one */
+            oldindent = Max(oldindent, newindent + 1);
+            while (newindent < oldindent)
+            {
+                int            newindent_if_tab = newindent + 8 - (newindent % 8);
+
+                if (newindent_if_tab <= oldindent)
+                {
+                    appendPQExpBufferChar(newline, '\t');
+                    newindent = newindent_if_tab;
+                }
+                else
+                {
+                    appendPQExpBufferChar(newline, ' ');
+                    newindent++;
+                }
+            }
+            /* and finally append the old comment */
+            appendPQExpBufferStr(newline, where);
+            /* we'll have appended the original newline; don't add another */
+        }
+        else
+            appendPQExpBufferChar(newline, '\n');
+
+        result[i] = newline->data;
+
+        break;                    /* assume there's only one match */
+    }
+
+    if (i >= numlines)
+    {
+        /*
+         * No match, so append a new entry.  (We rely on the bootstrap server
+         * to complain if it's not a valid GUC name.)
+         */
+        appendPQExpBufferChar(newline, '\n');
+        result[numlines++] = newline->data;
+        result[numlines] = NULL;    /* keep the array null-terminated */
+    }
+
+    return result;
+}
+
+/*
+ * Decide if we should quote a replacement GUC value.  We aren't too tense
+ * here, but we'd like to avoid quoting simple identifiers and numbers
+ * with units, which are common cases.
+ */
+static bool
+guc_value_requires_quotes(const char *guc_value)
+{
+    /* Don't use <ctype.h> macros here, they might accept too much */
+#define LETTERS    "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
+#define DIGITS    "0123456789"
+
+    if (*guc_value == '\0')
+        return true;            /* empty string must be quoted */
+    if (strchr(LETTERS, *guc_value))
+    {
+        if (strspn(guc_value, LETTERS DIGITS) == strlen(guc_value))
+            return false;        /* it's an identifier */
+        return true;            /* nope */
+    }
+    if (strchr(DIGITS, *guc_value))
+    {
+        /* skip over digits */
+        guc_value += strspn(guc_value, DIGITS);
+        /* there can be zero or more unit letters after the digits */
+        if (strspn(guc_value, LETTERS) == strlen(guc_value))
+            return false;        /* it's a number, possibly with units */
+        return true;            /* nope */
+    }
+    return true;                /* all else must be quoted */
+}
+
 /*
  * get the lines from a text file
  */
@@ -873,11 +1074,9 @@ test_config_settings(void)
         400, 300, 200, 100, 50
     };

-    char        cmd[MAXPGPATH];
     const int    connslen = sizeof(trial_conns) / sizeof(int);
     const int    bufslen = sizeof(trial_bufs) / sizeof(int);
     int            i,
-                status,
                 test_conns,
                 test_buffs,
                 ok_buffers = 0;
@@ -903,19 +1102,7 @@ test_config_settings(void)
         test_conns = trial_conns[i];
         test_buffs = MIN_BUFS_FOR_CONNS(test_conns);

-        snprintf(cmd, sizeof(cmd),
-                 "\"%s\" --check %s %s "
-                 "-c max_connections=%d "
-                 "-c shared_buffers=%d "
-                 "-c dynamic_shared_memory_type=%s "
-                 "< \"%s\" > \"%s\" 2>&1",
-                 backend_exec, boot_options, extra_options,
-                 test_conns, test_buffs,
-                 dynamic_shared_memory_type,
-                 DEVNULL, DEVNULL);
-        fflush(NULL);
-        status = system(cmd);
-        if (status == 0)
+        if (test_specific_config_settings(test_conns, test_buffs))
         {
             ok_buffers = test_buffs;
             break;
@@ -940,19 +1127,7 @@ test_config_settings(void)
             break;
         }

-        snprintf(cmd, sizeof(cmd),
-                 "\"%s\" --check %s %s "
-                 "-c max_connections=%d "
-                 "-c shared_buffers=%d "
-                 "-c dynamic_shared_memory_type=%s "
-                 "< \"%s\" > \"%s\" 2>&1",
-                 backend_exec, boot_options, extra_options,
-                 n_connections, test_buffs,
-                 dynamic_shared_memory_type,
-                 DEVNULL, DEVNULL);
-        fflush(NULL);
-        status = system(cmd);
-        if (status == 0)
+        if (test_specific_config_settings(n_connections, test_buffs))
             break;
     }
     n_buffers = test_buffs;
@@ -968,6 +1143,48 @@ test_config_settings(void)
     printf("%s\n", default_timezone ? default_timezone : "GMT");
 }

+/*
+ * Test a specific combination of configuration settings.
+ */
+static bool
+test_specific_config_settings(int test_conns, int test_buffs)
+{
+    PQExpBuffer cmd = createPQExpBuffer();
+    _stringlist *gnames,
+               *gvalues;
+    int            status;
+
+    /* Set up the test postmaster invocation */
+    printfPQExpBuffer(cmd,
+                      "\"%s\" --check %s %s "
+                      "-c max_connections=%d "
+                      "-c shared_buffers=%d "
+                      "-c dynamic_shared_memory_type=%s",
+                      backend_exec, boot_options, extra_options,
+                      test_conns, test_buffs,
+                      dynamic_shared_memory_type);
+
+    /* Add any user-given setting overrides */
+    for (gnames = extra_guc_names, gvalues = extra_guc_values;
+         gnames != NULL;        /* assume lists have the same length */
+         gnames = gnames->next, gvalues = gvalues->next)
+    {
+        appendPQExpBuffer(cmd, " -c %s=", gnames->str);
+        appendShellString(cmd, gvalues->str);
+    }
+
+    appendPQExpBuffer(cmd,
+                      " < \"%s\" > \"%s\" 2>&1",
+                      DEVNULL, DEVNULL);
+
+    fflush(NULL);
+    status = system(cmd->data);
+
+    destroyPQExpBuffer(cmd);
+
+    return (status == 0);
+}
+
 /*
  * Calculate the default wal_size with a "pretty" unit.
  */
@@ -995,6 +1212,8 @@ setup_config(void)
     char        repltok[MAXPGPATH];
     char        path[MAXPGPATH];
     char       *autoconflines[3];
+    _stringlist *gnames,
+               *gvalues;

     fputs(_("creating configuration files ... "), stdout);
     fflush(stdout);
@@ -1003,120 +1222,106 @@ setup_config(void)

     conflines = readfile(conf_file);

-    snprintf(repltok, sizeof(repltok), "max_connections = %d", n_connections);
-    conflines = replace_token(conflines, "#max_connections = 100", repltok);
+    snprintf(repltok, sizeof(repltok), "%d", n_connections);
+    conflines = replace_guc_value(conflines, "max_connections",
+                                  repltok, false);

     if ((n_buffers * (BLCKSZ / 1024)) % 1024 == 0)
-        snprintf(repltok, sizeof(repltok), "shared_buffers = %dMB",
+        snprintf(repltok, sizeof(repltok), "%dMB",
                  (n_buffers * (BLCKSZ / 1024)) / 1024);
     else
-        snprintf(repltok, sizeof(repltok), "shared_buffers = %dkB",
+        snprintf(repltok, sizeof(repltok), "%dkB",
                  n_buffers * (BLCKSZ / 1024));
-    conflines = replace_token(conflines, "#shared_buffers = 128MB", repltok);
+    conflines = replace_guc_value(conflines, "shared_buffers",
+                                  repltok, false);

-    snprintf(repltok, sizeof(repltok), "#unix_socket_directories = '%s'",
-             DEFAULT_PGSOCKET_DIR);
-    conflines = replace_token(conflines, "#unix_socket_directories = '/tmp'",
-                              repltok);
+    conflines = replace_guc_value(conflines, "lc_messages", lc_messages, false);

-#if DEF_PGPORT != 5432
-    snprintf(repltok, sizeof(repltok), "#port = %d", DEF_PGPORT);
-    conflines = replace_token(conflines, "#port = 5432", repltok);
-#endif
+    conflines = replace_guc_value(conflines, "lc_monetary", lc_monetary, false);

-    /* set default max_wal_size and min_wal_size */
-    snprintf(repltok, sizeof(repltok), "min_wal_size = %s",
-             pretty_wal_size(DEFAULT_MIN_WAL_SEGS));
-    conflines = replace_token(conflines, "#min_wal_size = 80MB", repltok);
+    conflines = replace_guc_value(conflines, "lc_numeric", lc_numeric, false);

-    snprintf(repltok, sizeof(repltok), "max_wal_size = %s",
-             pretty_wal_size(DEFAULT_MAX_WAL_SEGS));
-    conflines = replace_token(conflines, "#max_wal_size = 1GB", repltok);
-
-    snprintf(repltok, sizeof(repltok), "lc_messages = '%s'",
-             escape_quotes(lc_messages));
-    conflines = replace_token(conflines, "#lc_messages = 'C'", repltok);
-
-    snprintf(repltok, sizeof(repltok), "lc_monetary = '%s'",
-             escape_quotes(lc_monetary));
-    conflines = replace_token(conflines, "#lc_monetary = 'C'", repltok);
-
-    snprintf(repltok, sizeof(repltok), "lc_numeric = '%s'",
-             escape_quotes(lc_numeric));
-    conflines = replace_token(conflines, "#lc_numeric = 'C'", repltok);
-
-    snprintf(repltok, sizeof(repltok), "lc_time = '%s'",
-             escape_quotes(lc_time));
-    conflines = replace_token(conflines, "#lc_time = 'C'", repltok);
+    conflines = replace_guc_value(conflines, "lc_time", lc_time, false);

     switch (locale_date_order(lc_time))
     {
         case DATEORDER_YMD:
-            strcpy(repltok, "datestyle = 'iso, ymd'");
+            strcpy(repltok, "iso, ymd");
             break;
         case DATEORDER_DMY:
-            strcpy(repltok, "datestyle = 'iso, dmy'");
+            strcpy(repltok, "iso, dmy");
             break;
         case DATEORDER_MDY:
         default:
-            strcpy(repltok, "datestyle = 'iso, mdy'");
+            strcpy(repltok, "iso, mdy");
             break;
     }
-    conflines = replace_token(conflines, "#datestyle = 'iso, mdy'", repltok);
+    conflines = replace_guc_value(conflines, "datestyle", repltok, false);

     snprintf(repltok, sizeof(repltok),
-             "default_text_search_config = 'pg_catalog.%s'",
-             escape_quotes(default_text_search_config));
-    conflines = replace_token(conflines,
-                              "#default_text_search_config = 'pg_catalog.simple'",
-                              repltok);
+             "pg_catalog.%s", default_text_search_config);
+    conflines = replace_guc_value(conflines, "default_text_search_config",
+                                  repltok, false);

     if (default_timezone)
     {
-        snprintf(repltok, sizeof(repltok), "timezone = '%s'",
-                 escape_quotes(default_timezone));
-        conflines = replace_token(conflines, "#timezone = 'GMT'", repltok);
-        snprintf(repltok, sizeof(repltok), "log_timezone = '%s'",
-                 escape_quotes(default_timezone));
-        conflines = replace_token(conflines, "#log_timezone = 'GMT'", repltok);
+        conflines = replace_guc_value(conflines, "timezone",
+                                      default_timezone, false);
+        conflines = replace_guc_value(conflines, "log_timezone",
+                                      default_timezone, false);
     }

-    snprintf(repltok, sizeof(repltok), "dynamic_shared_memory_type = %s",
-             dynamic_shared_memory_type);
-    conflines = replace_token(conflines, "#dynamic_shared_memory_type = posix",
-                              repltok);
+    conflines = replace_guc_value(conflines, "dynamic_shared_memory_type",
+                                  dynamic_shared_memory_type, false);
+
+    /*
+     * Fix up various entries to match the true compile-time defaults.  Since
+     * these are indeed defaults, keep the postgresql.conf lines commented.
+     */
+    conflines = replace_guc_value(conflines, "unix_socket_directories",
+                                  DEFAULT_PGSOCKET_DIR, true);
+
+#if DEF_PGPORT != 5432
+    snprintf(repltok, sizeof(repltok), "%d", DEF_PGPORT);
+    conflines = replace_guc_value(conflines, "port",
+                                  repltok, true);
+#endif
+
+    conflines = replace_guc_value(conflines, "min_wal_size",
+                                  pretty_wal_size(DEFAULT_MIN_WAL_SEGS), true);
+
+    conflines = replace_guc_value(conflines, "max_wal_size",
+                                  pretty_wal_size(DEFAULT_MAX_WAL_SEGS), true);

 #if DEFAULT_BACKEND_FLUSH_AFTER > 0
-    snprintf(repltok, sizeof(repltok), "#backend_flush_after = %dkB",
+    snprintf(repltok, sizeof(repltok), "%dkB",
              DEFAULT_BACKEND_FLUSH_AFTER * (BLCKSZ / 1024));
-    conflines = replace_token(conflines, "#backend_flush_after = 0",
-                              repltok);
+    conflines = replace_guc_value(conflines, "backend_flush_after",
+                                  repltok, true);
 #endif

 #if DEFAULT_BGWRITER_FLUSH_AFTER > 0
-    snprintf(repltok, sizeof(repltok), "#bgwriter_flush_after = %dkB",
+    snprintf(repltok, sizeof(repltok), "%dkB",
              DEFAULT_BGWRITER_FLUSH_AFTER * (BLCKSZ / 1024));
-    conflines = replace_token(conflines, "#bgwriter_flush_after = 0",
-                              repltok);
+    conflines = replace_guc_value(conflines, "bgwriter_flush_after",
+                                  repltok, true);
 #endif

 #if DEFAULT_CHECKPOINT_FLUSH_AFTER > 0
-    snprintf(repltok, sizeof(repltok), "#checkpoint_flush_after = %dkB",
+    snprintf(repltok, sizeof(repltok), "%dkB",
              DEFAULT_CHECKPOINT_FLUSH_AFTER * (BLCKSZ / 1024));
-    conflines = replace_token(conflines, "#checkpoint_flush_after = 0",
-                              repltok);
+    conflines = replace_guc_value(conflines, "checkpoint_flush_after",
+                                  repltok, true);
 #endif

 #ifndef USE_PREFETCH
-    conflines = replace_token(conflines,
-                              "#effective_io_concurrency = 1",
-                              "#effective_io_concurrency = 0");
+    conflines = replace_guc_value(conflines, "effective_io_concurrency",
+                                  "0", true);
 #endif

 #ifdef WIN32
-    conflines = replace_token(conflines,
-                              "#update_process_title = on",
-                              "#update_process_title = off");
+    conflines = replace_guc_value(conflines, "update_process_title",
+                                  "off", true);
 #endif

     /*
@@ -1128,9 +1333,8 @@ setup_config(void)
         (strcmp(authmethodhost, "md5") == 0 &&
          strcmp(authmethodlocal, "scram-sha-256") != 0))
     {
-        conflines = replace_token(conflines,
-                                  "#password_encryption = scram-sha-256",
-                                  "password_encryption = md5");
+        conflines = replace_guc_value(conflines, "password_encryption",
+                                      "md5", false);
     }

     /*
@@ -1141,11 +1345,22 @@ setup_config(void)
      */
     if (pg_dir_create_mode == PG_DIR_MODE_GROUP)
     {
-        conflines = replace_token(conflines,
-                                  "#log_file_mode = 0600",
-                                  "log_file_mode = 0640");
+        conflines = replace_guc_value(conflines, "log_file_mode",
+                                      "0640", false);
     }

+    /*
+     * Now replace anything that's overridden via -c switches.
+     */
+    for (gnames = extra_guc_names, gvalues = extra_guc_values;
+         gnames != NULL;        /* assume lists have the same length */
+         gnames = gnames->next, gvalues = gvalues->next)
+    {
+        conflines = replace_guc_value(conflines, gnames->str,
+                                      gvalues->str, false);
+    }
+
+    /* ... and write out the finished postgresql.conf file */
     snprintf(path, sizeof(path), "%s/postgresql.conf", pg_data);

     writefile(path, conflines);
@@ -2124,6 +2339,7 @@ usage(const char *progname)
     printf(_("  -X, --waldir=WALDIR       location for the write-ahead log directory\n"));
     printf(_("      --wal-segsize=SIZE    size of WAL segments, in megabytes\n"));
     printf(_("\nLess commonly used options:\n"));
+    printf(_("  -c, --set NAME=VALUE      override default setting for server parameter\n"));
     printf(_("  -d, --debug               generate lots of debugging output\n"));
     printf(_("      --discard-caches      set debug_discard_caches=1\n"));
     printf(_("  -L DIRECTORY              where to find the input files\n"));
@@ -2759,6 +2975,7 @@ main(int argc, char *argv[])
         {"nosync", no_argument, NULL, 'N'}, /* for backwards compatibility */
         {"no-sync", no_argument, NULL, 'N'},
         {"no-instructions", no_argument, NULL, 13},
+        {"set", required_argument, NULL, 'c'},
         {"sync-only", no_argument, NULL, 'S'},
         {"waldir", required_argument, NULL, 'X'},
         {"wal-segsize", required_argument, NULL, 12},
@@ -2808,7 +3025,8 @@ main(int argc, char *argv[])

     /* process command-line options */

-    while ((c = getopt_long(argc, argv, "A:dD:E:gkL:nNsST:U:WX:", long_options, &option_index)) != -1)
+    while ((c = getopt_long(argc, argv, "A:c:dD:E:gkL:nNsST:U:WX:",
+                            long_options, &option_index)) != -1)
     {
         switch (c)
         {
@@ -2831,6 +3049,24 @@ main(int argc, char *argv[])
             case 11:
                 authmethodhost = pg_strdup(optarg);
                 break;
+            case 'c':
+                {
+                    char       *buf = pg_strdup(optarg);
+                    char       *equals = strchr(buf, '=');
+
+                    if (!equals)
+                    {
+                        pg_log_error("-c %s requires a value", buf);
+                        pg_log_error_hint("Try \"%s --help\" for more information.",
+                                          progname);
+                        exit(1);
+                    }
+                    *equals++ = '\0';    /* terminate variable name */
+                    add_stringlist_item(&extra_guc_names, buf);
+                    add_stringlist_item(&extra_guc_values, equals);
+                    pfree(buf);
+                }
+                break;
             case 'D':
                 pg_data = pg_strdup(optarg);
                 break;
diff --git a/src/bin/initdb/t/001_initdb.pl b/src/bin/initdb/t/001_initdb.pl
index 772769acab..1e23ac8d0c 100644
--- a/src/bin/initdb/t/001_initdb.pl
+++ b/src/bin/initdb/t/001_initdb.pl
@@ -48,7 +48,13 @@ mkdir $datadir;
     local (%ENV) = %ENV;
     delete $ENV{TZ};

-    command_ok([ 'initdb', '-N', '-T', 'german', '-X', $xlogdir, $datadir ],
+    # while we are here, also exercise -T and -c options
+    command_ok(
+        [
+            'initdb', '-N', '-T', 'german', '-c',
+            'default_text_search_config=german',
+            '-X', $xlogdir, $datadir
+        ],
         'successful creation');

     # Permissions on PGDATA should be default
@@ -147,4 +153,7 @@ command_fails(
     ],
     'fails for invalid option combination');

+command_fails([ 'initdb', '--no-sync', '--set', 'foo=bar', "$tempdir/dataX" ],
+    'fails for invalid --set option');
+
 done_testing();

В списке pgsql-hackers по дате отправления:

Предыдущее
От: Robert Haas
Дата:
Сообщение: Re: Non-superuser subscription owners
Следующее
От: Robert Haas
Дата:
Сообщение: Re: Set arbitrary GUC options during initdb