Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1pLUvS-00081W-RW for pgsql-hackers@arkaria.postgresql.org; Fri, 27 Jan 2023 20:02:19 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.92) (envelope-from ) id 1pLUvR-0007JZ-5H for pgsql-hackers@arkaria.postgresql.org; Fri, 27 Jan 2023 20:02:17 +0000 Received: from magus.postgresql.org ([2a02:c0:301:0:ffff::29]) by malur.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1pLUvQ-0007JP-Q5 for pgsql-hackers@lists.postgresql.org; Fri, 27 Jan 2023 20:02:16 +0000 Received: from sss.pgh.pa.us ([66.207.139.130]) by magus.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1pLUvJ-0002oO-Q1 for pgsql-hackers@lists.postgresql.org; Fri, 27 Jan 2023 20:02:16 +0000 Received: from sss1.sss.pgh.pa.us (localhost [127.0.0.1]) by sss.pgh.pa.us (8.15.2/8.15.2) with ESMTP id 30RK24lF3527271; Fri, 27 Jan 2023 15:02:04 -0500 From: Tom Lane To: Robert Haas cc: "David G. Johnston" , Peter Eisentraut , pgsql-hackers@lists.postgresql.org Subject: Re: Set arbitrary GUC options during initdb In-reply-to: <3337560.1674837348@sss.pgh.pa.us> References: <2844176.1674681919@sss.pgh.pa.us> <8a8fda29-5a68-ba68-a2ff-8d17602b3762@enterprisedb.com> <3331317.1674833645@sss.pgh.pa.us> <3332912.1674834822@sss.pgh.pa.us> <3337560.1674837348@sss.pgh.pa.us> Comments: In-reply-to Tom Lane message dated "Fri, 27 Jan 2023 11:35:48 -0500" MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="----- =_aaaaaaaaaa0" Content-ID: <3525123.1674849711.0@sss.pgh.pa.us> Date: Fri, 27 Jan 2023 15:02:04 -0500 Message-ID: <3527270.1674849724@sss.pgh.pa.us> List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk ------- =_aaaaaaaaaa0 Content-Type: text/plain; charset="us-ascii" Content-ID: <3525123.1674849711.1@sss.pgh.pa.us> 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 ------- =_aaaaaaaaaa0 Content-Type: text/x-diff; name="set-arbitrary-options-during-initdb-2.patch"; charset="us-ascii" Content-ID: <3525123.1674849711.2@sss.pgh.pa.us> Content-Description: set-arbitrary-options-during-initdb-2.patch Content-Transfer-Encoding: quoted-printable 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: = + + + + + + Forcibly set the server parameter name + to value during initdb, + and also install that setting in the + generated postgresql.conf 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 serv= er + will not start at all using the default parameters. + + + + 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 #include #include #include @@ -82,6 +83,13 @@ /* Ideally this would be in a .h file, but it hardly seems worth the trou= ble */ 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[] =3D { "trust", "reject", "scram-sha-256", "md5", "password", "ident", "radius"= , #ifdef ENABLE_GSS @@ -142,6 +150,8 @@ static char *pwfilename =3D NULL; static char *superuser_password =3D NULL; static const char *authmethodhost =3D NULL; static const char *authmethodlocal =3D NULL; +static _stringlist *extra_guc_names =3D NULL; +static _stringlist *extra_guc_values =3D NULL; static bool debug =3D false; static bool noclean =3D false; static bool noinstructions =3D 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 =3D pg_malloc(sizeof(_stringlist)); + _stringlist *oldentry; + + newentry->str =3D pg_strdup(str); + newentry->next =3D NULL; + if (*listhead =3D=3D NULL) + *listhead =3D newentry; + else + { + for (oldentry =3D *listhead; oldentry->next; oldentry =3D oldentry->nex= t) + /* skip */ ; + oldentry->next =3D 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-ou= t + * 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 matc= h, + * 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_val= ue, + bool mark_as_comment) +{ + char **result; + int namelen =3D strlen(guc_name); + PQExpBuffer newline =3D createPQExpBuffer(); + int numlines =3D 0; + int i; + + /* prepare the replacement line, except for possible comment and newline= */ + if (mark_as_comment) + appendPQExpBufferChar(newline, '#'); + appendPQExpBuffer(newline, "%s =3D ", 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 =3D 0; lines[i]; i++) + numlines++; + + /* leave room for one extra string in case we need to append */ + result =3D (char **) pg_malloc((numlines + 2) * sizeof(char *)); + + /* initialize result with all the same strings */ + memcpy(result, lines, (numlines + 1) * sizeof(char *)); + + for (i =3D 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 =3D result[i]; + while (*where =3D=3D '#' || isspace((unsigned char) *where)) + where++; + if (strncmp(where, guc_name, namelen) !=3D 0) + continue; + where +=3D namelen; + while (isspace((unsigned char) *where)) + where++; + if (*where !=3D '=3D') + continue; + + /* found it -- append the original comment if any */ + where =3D 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 =3D 0; + int newindent; + + for (ptr =3D result[i]; ptr < where; ptr++) + { + if (*ptr =3D=3D '\t') + oldindent +=3D 8 - (oldindent % 8); + else + oldindent++; + } + /* ignore the possibility of tabs in guc_value */ + newindent =3D newline->len; + /* append appropriate tabs and spaces, forcing at least one */ + oldindent =3D Max(oldindent, newindent + 1); + while (newindent < oldindent) + { + int newindent_if_tab =3D newindent + 8 - (newindent % 8); + + if (newindent_if_tab <=3D oldindent) + { + appendPQExpBufferChar(newline, '\t'); + newindent =3D 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] =3D newline->data; + + break; /* assume there's only one match */ + } + + if (i >=3D 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++] =3D newline->data; + result[numlines] =3D NULL; /* keep the array null-terminated */ + } + + return result; +} + +/* + * Decide if we should quote a replacement GUC value. We aren't too tens= e + * 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 macros here, they might accept too much */ +#define LETTERS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" +#define DIGITS "0123456789" + + if (*guc_value =3D=3D '\0') + return true; /* empty string must be quoted */ + if (strchr(LETTERS, *guc_value)) + { + if (strspn(guc_value, LETTERS DIGITS) =3D=3D strlen(guc_value)) + return false; /* it's an identifier */ + return true; /* nope */ + } + if (strchr(DIGITS, *guc_value)) + { + /* skip over digits */ + guc_value +=3D strspn(guc_value, DIGITS); + /* there can be zero or more unit letters after the digits */ + if (strspn(guc_value, LETTERS) =3D=3D 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 =3D sizeof(trial_conns) / sizeof(int); const int bufslen =3D sizeof(trial_bufs) / sizeof(int); int i, - status, test_conns, test_buffs, ok_buffers =3D 0; @@ -903,19 +1102,7 @@ test_config_settings(void) test_conns =3D trial_conns[i]; test_buffs =3D MIN_BUFS_FOR_CONNS(test_conns); = - snprintf(cmd, sizeof(cmd), - "\"%s\" --check %s %s " - "-c max_connections=3D%d " - "-c shared_buffers=3D%d " - "-c dynamic_shared_memory_type=3D%s " - "< \"%s\" > \"%s\" 2>&1", - backend_exec, boot_options, extra_options, - test_conns, test_buffs, - dynamic_shared_memory_type, - DEVNULL, DEVNULL); - fflush(NULL); - status =3D system(cmd); - if (status =3D=3D 0) + if (test_specific_config_settings(test_conns, test_buffs)) { ok_buffers =3D test_buffs; break; @@ -940,19 +1127,7 @@ test_config_settings(void) break; } = - snprintf(cmd, sizeof(cmd), - "\"%s\" --check %s %s " - "-c max_connections=3D%d " - "-c shared_buffers=3D%d " - "-c dynamic_shared_memory_type=3D%s " - "< \"%s\" > \"%s\" 2>&1", - backend_exec, boot_options, extra_options, - n_connections, test_buffs, - dynamic_shared_memory_type, - DEVNULL, DEVNULL); - fflush(NULL); - status =3D system(cmd); - if (status =3D=3D 0) + if (test_specific_config_settings(n_connections, test_buffs)) break; } n_buffers =3D 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 =3D createPQExpBuffer(); + _stringlist *gnames, + *gvalues; + int status; + + /* Set up the test postmaster invocation */ + printfPQExpBuffer(cmd, + "\"%s\" --check %s %s " + "-c max_connections=3D%d " + "-c shared_buffers=3D%d " + "-c dynamic_shared_memory_type=3D%s", + backend_exec, boot_options, extra_options, + test_conns, test_buffs, + dynamic_shared_memory_type); + + /* Add any user-given setting overrides */ + for (gnames =3D extra_guc_names, gvalues =3D extra_guc_values; + gnames !=3D NULL; /* assume lists have the same length */ + gnames =3D gnames->next, gvalues =3D gvalues->next) + { + appendPQExpBuffer(cmd, " -c %s=3D", gnames->str); + appendShellString(cmd, gvalues->str); + } + + appendPQExpBuffer(cmd, + " < \"%s\" > \"%s\" 2>&1", + DEVNULL, DEVNULL); + + fflush(NULL); + status =3D system(cmd->data); + + destroyPQExpBuffer(cmd); + + return (status =3D=3D 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 =3D readfile(conf_file); = - snprintf(repltok, sizeof(repltok), "max_connections =3D %d", n_connectio= ns); - conflines =3D replace_token(conflines, "#max_connections =3D 100", replt= ok); + snprintf(repltok, sizeof(repltok), "%d", n_connections); + conflines =3D replace_guc_value(conflines, "max_connections", + repltok, false); = if ((n_buffers * (BLCKSZ / 1024)) % 1024 =3D=3D 0) - snprintf(repltok, sizeof(repltok), "shared_buffers =3D %dMB", + snprintf(repltok, sizeof(repltok), "%dMB", (n_buffers * (BLCKSZ / 1024)) / 1024); else - snprintf(repltok, sizeof(repltok), "shared_buffers =3D %dkB", + snprintf(repltok, sizeof(repltok), "%dkB", n_buffers * (BLCKSZ / 1024)); - conflines =3D replace_token(conflines, "#shared_buffers =3D 128MB", repl= tok); + conflines =3D replace_guc_value(conflines, "shared_buffers", + repltok, false); = - snprintf(repltok, sizeof(repltok), "#unix_socket_directories =3D '%s'", - DEFAULT_PGSOCKET_DIR); - conflines =3D replace_token(conflines, "#unix_socket_directories =3D '/t= mp'", - repltok); + conflines =3D replace_guc_value(conflines, "lc_messages", lc_messages, f= alse); = -#if DEF_PGPORT !=3D 5432 - snprintf(repltok, sizeof(repltok), "#port =3D %d", DEF_PGPORT); - conflines =3D replace_token(conflines, "#port =3D 5432", repltok); -#endif + conflines =3D replace_guc_value(conflines, "lc_monetary", lc_monetary, f= alse); = - /* set default max_wal_size and min_wal_size */ - snprintf(repltok, sizeof(repltok), "min_wal_size =3D %s", - pretty_wal_size(DEFAULT_MIN_WAL_SEGS)); - conflines =3D replace_token(conflines, "#min_wal_size =3D 80MB", repltok= ); + conflines =3D replace_guc_value(conflines, "lc_numeric", lc_numeric, fal= se); = - snprintf(repltok, sizeof(repltok), "max_wal_size =3D %s", - pretty_wal_size(DEFAULT_MAX_WAL_SEGS)); - conflines =3D replace_token(conflines, "#max_wal_size =3D 1GB", repltok)= ; - - snprintf(repltok, sizeof(repltok), "lc_messages =3D '%s'", - escape_quotes(lc_messages)); - conflines =3D replace_token(conflines, "#lc_messages =3D 'C'", repltok); - - snprintf(repltok, sizeof(repltok), "lc_monetary =3D '%s'", - escape_quotes(lc_monetary)); - conflines =3D replace_token(conflines, "#lc_monetary =3D 'C'", repltok); - - snprintf(repltok, sizeof(repltok), "lc_numeric =3D '%s'", - escape_quotes(lc_numeric)); - conflines =3D replace_token(conflines, "#lc_numeric =3D 'C'", repltok); - - snprintf(repltok, sizeof(repltok), "lc_time =3D '%s'", - escape_quotes(lc_time)); - conflines =3D replace_token(conflines, "#lc_time =3D 'C'", repltok); + conflines =3D replace_guc_value(conflines, "lc_time", lc_time, false); = switch (locale_date_order(lc_time)) { case DATEORDER_YMD: - strcpy(repltok, "datestyle =3D 'iso, ymd'"); + strcpy(repltok, "iso, ymd"); break; case DATEORDER_DMY: - strcpy(repltok, "datestyle =3D 'iso, dmy'"); + strcpy(repltok, "iso, dmy"); break; case DATEORDER_MDY: default: - strcpy(repltok, "datestyle =3D 'iso, mdy'"); + strcpy(repltok, "iso, mdy"); break; } - conflines =3D replace_token(conflines, "#datestyle =3D 'iso, mdy'", repl= tok); + conflines =3D replace_guc_value(conflines, "datestyle", repltok, false); = snprintf(repltok, sizeof(repltok), - "default_text_search_config =3D 'pg_catalog.%s'", - escape_quotes(default_text_search_config)); - conflines =3D replace_token(conflines, - "#default_text_search_config =3D 'pg_catalog.simple'", - repltok); + "pg_catalog.%s", default_text_search_config); + conflines =3D replace_guc_value(conflines, "default_text_search_config", + repltok, false); = if (default_timezone) { - snprintf(repltok, sizeof(repltok), "timezone =3D '%s'", - escape_quotes(default_timezone)); - conflines =3D replace_token(conflines, "#timezone =3D 'GMT'", repltok); - snprintf(repltok, sizeof(repltok), "log_timezone =3D '%s'", - escape_quotes(default_timezone)); - conflines =3D replace_token(conflines, "#log_timezone =3D 'GMT'", replt= ok); + conflines =3D replace_guc_value(conflines, "timezone", + default_timezone, false); + conflines =3D replace_guc_value(conflines, "log_timezone", + default_timezone, false); } = - snprintf(repltok, sizeof(repltok), "dynamic_shared_memory_type =3D %s", - dynamic_shared_memory_type); - conflines =3D replace_token(conflines, "#dynamic_shared_memory_type =3D = posix", - repltok); + conflines =3D replace_guc_value(conflines, "dynamic_shared_memory_type", + dynamic_shared_memory_type, false); + + /* + * Fix up various entries to match the true compile-time defaults. Sinc= e + * these are indeed defaults, keep the postgresql.conf lines commented. + */ + conflines =3D replace_guc_value(conflines, "unix_socket_directories", + DEFAULT_PGSOCKET_DIR, true); + +#if DEF_PGPORT !=3D 5432 + snprintf(repltok, sizeof(repltok), "%d", DEF_PGPORT); + conflines =3D replace_guc_value(conflines, "port", + repltok, true); +#endif + + conflines =3D replace_guc_value(conflines, "min_wal_size", + pretty_wal_size(DEFAULT_MIN_WAL_SEGS), true); + + conflines =3D 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 =3D %dkB", + snprintf(repltok, sizeof(repltok), "%dkB", DEFAULT_BACKEND_FLUSH_AFTER * (BLCKSZ / 1024)); - conflines =3D replace_token(conflines, "#backend_flush_after =3D 0", - repltok); + conflines =3D replace_guc_value(conflines, "backend_flush_after", + repltok, true); #endif = #if DEFAULT_BGWRITER_FLUSH_AFTER > 0 - snprintf(repltok, sizeof(repltok), "#bgwriter_flush_after =3D %dkB", + snprintf(repltok, sizeof(repltok), "%dkB", DEFAULT_BGWRITER_FLUSH_AFTER * (BLCKSZ / 1024)); - conflines =3D replace_token(conflines, "#bgwriter_flush_after =3D 0", - repltok); + conflines =3D replace_guc_value(conflines, "bgwriter_flush_after", + repltok, true); #endif = #if DEFAULT_CHECKPOINT_FLUSH_AFTER > 0 - snprintf(repltok, sizeof(repltok), "#checkpoint_flush_after =3D %dkB", + snprintf(repltok, sizeof(repltok), "%dkB", DEFAULT_CHECKPOINT_FLUSH_AFTER * (BLCKSZ / 1024)); - conflines =3D replace_token(conflines, "#checkpoint_flush_after =3D 0", - repltok); + conflines =3D replace_guc_value(conflines, "checkpoint_flush_after", + repltok, true); #endif = #ifndef USE_PREFETCH - conflines =3D replace_token(conflines, - "#effective_io_concurrency =3D 1", - "#effective_io_concurrency =3D 0"); + conflines =3D replace_guc_value(conflines, "effective_io_concurrency", + "0", true); #endif = #ifdef WIN32 - conflines =3D replace_token(conflines, - "#update_process_title =3D on", - "#update_process_title =3D off"); + conflines =3D replace_guc_value(conflines, "update_process_title", + "off", true); #endif = /* @@ -1128,9 +1333,8 @@ setup_config(void) (strcmp(authmethodhost, "md5") =3D=3D 0 && strcmp(authmethodlocal, "scram-sha-256") !=3D 0)) { - conflines =3D replace_token(conflines, - "#password_encryption =3D scram-sha-256", - "password_encryption =3D md5"); + conflines =3D replace_guc_value(conflines, "password_encryption", + "md5", false); } = /* @@ -1141,11 +1345,22 @@ setup_config(void) */ if (pg_dir_create_mode =3D=3D PG_DIR_MODE_GROUP) { - conflines =3D replace_token(conflines, - "#log_file_mode =3D 0600", - "log_file_mode =3D 0640"); + conflines =3D replace_guc_value(conflines, "log_file_mode", + "0640", false); } = + /* + * Now replace anything that's overridden via -c switches. + */ + for (gnames =3D extra_guc_names, gvalues =3D extra_guc_values; + gnames !=3D NULL; /* assume lists have the same length */ + gnames =3D gnames->next, gvalues =3D gvalues->next) + { + conflines =3D 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=3DWALDIR location for the write-ahead log= directory\n")); printf(_(" --wal-segsize=3DSIZE size of WAL segments, in megabyt= es\n")); printf(_("\nLess commonly used options:\n")); + printf(_(" -c, --set NAME=3DVALUE override default setting for ser= ver parameter\n")); printf(_(" -d, --debug generate lots of debugging output\= n")); printf(_(" --discard-caches set debug_discard_caches=3D1\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 =3D getopt_long(argc, argv, "A:dD:E:gkL:nNsST:U:WX:", long_opt= ions, &option_index)) !=3D -1) + while ((c =3D getopt_long(argc, argv, "A:c:dD:E:gkL:nNsST:U:WX:", + long_options, &option_index)) !=3D -1) { switch (c) { @@ -2831,6 +3049,24 @@ main(int argc, char *argv[]) case 11: authmethodhost =3D pg_strdup(optarg); break; + case 'c': + { + char *buf =3D pg_strdup(optarg); + char *equals =3D strchr(buf, '=3D'); + + 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++ =3D '\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 =3D 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) =3D %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=3Dgerman', + '-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=3Dbar', "$tempdir/da= taX" ], + 'fails for invalid --set option'); + done_testing(); ------- =_aaaaaaaaaa0--