pg_change_conf.patch
application/octet-stream
Filename: pg_change_conf.patch
Type: application/octet-stream
Part: 0
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: context
| File | + | − |
|---|---|---|
| src/backend/commands/dbcommands.c | 388 | 0 |
| src/backend/postmaster/postmaster.c | 8 | 0 |
| src/backend/replication/basebackup.c | 6 | 0 |
| src/backend/tcop/utility.c | 2 | 0 |
| src/backend/utils/adt/misc.c | 22 | 0 |
| src/backend/utils/misc/guc.c | 150 | 0 |
| src/backend/utils/misc/guc-file.l | 136 | 0 |
| src/backend/utils/misc/postgresql.conf.sample | 0 | 1 |
| src/bin/initdb/initdb.c | 108 | 0 |
| src/include/access/xlog_internal.h | 1 | 0 |
| src/include/catalog/pg_proc.h | 2 | 0 |
| src/include/commands/dbcommands.h | 1 | 0 |
| src/include/nodes/parsenodes.h | 2 | 0 |
| src/include/utils/builtins.h | 1 | 0 |
| src/include/utils/guc.h | 9 | 0 |
| src/include/utils/guc_tables.h | 2 | 0 |
*** a/src/backend/commands/dbcommands.c
--- b/src/backend/commands/dbcommands.c
***************
*** 58,63 ****
--- 58,80 ----
#include "utils/snapmgr.h"
#include "utils/syscache.h"
#include "utils/tqual.h"
+ #include "utils/guc_tables.h"
+
+
+ /*lock file used in edit postgresql.conf.auto*/
+ struct lock_file
+ {
+ int fd;
+ char filename[MAXPGPATH];
+ };
+
+
+ struct strbuf
+ {
+ size_t len;
+ char *buf;
+ };
+
typedef struct
***************
*** 1434,1439 **** AlterDatabaseSet(AlterDatabaseSetStmt *stmt)
--- 1451,1827 ----
/*
+ * EDIT .AUTO.CONF FILE WITH FUNCTION pg_change_conf(key,value)...
+ */
+
+ /*create a .lock file for .auto.conf file so that it act as an lock to prevent concurrent edit on .auto.conf file*/
+ static int lock_confile(struct lock_file *lk, const char *path, int flags)
+ {
+ int ntries = 0;
+ strcpy(lk->filename, path);
+ strcat(lk->filename, CONF_LOCK_POSTFIX);
+ tryAgain:
+ lk->fd = open(lk->filename, O_RDWR | O_CREAT | O_EXCL, 0666);
+ if (0 > lk->fd)
+ {
+ /*
+ * Couldn't create the lock file. Probably it already exists. If so wait for some time
+ */
+
+ ntries++;
+ if ((errno != EEXIST && errno != EACCES) || ntries > 100)
+ {
+ if (errno != EEXIST)
+ {
+ ereport(LOG,
+ (errcode_for_file_access(),
+ errmsg("could not create lock file \"%s\": %m",
+ lk->filename)));
+ }
+ else
+ {
+ ereport(LOG,
+ (errcode_for_file_access(),
+ errmsg("could not create lock file \"%s\": %m ",lk->filename),
+ errhint("May be too many concurrent edit into file happening, please wait!! and retry"
+ "or .lock is file accidently left there please clean the file from config_dir")));
+ }
+
+ lk->filename[0] = 0;
+ }
+ else
+ {
+ pg_usleep(100000); /*in total wait for 10sec*/
+ goto tryAgain;
+ }
+ }
+
+ return lk->fd;
+ }
+
+ /*function to close the .lock file*/
+ int close_lock_file(struct lock_file *lk)
+ {
+ int fd = lk->fd;
+ lk->fd = -1;
+ return close(fd);
+ }
+
+ /*function to commit the changes done on element of .auto.conf file */
+ /* here we rename .lock to .auto.conf hence commit the changes to .auto.conf file */
+ int commit_lock_file(struct lock_file *lk)
+ {
+ char result_file[MAXPGPATH];
+ size_t i;
+ if (lk->fd >= 0 && close_lock_file(lk))
+ return -1;
+ strcpy(result_file, lk->filename);
+ i = strlen(result_file) - 5; /* remove .lock charecters */
+ result_file[i] = 0;
+ if (rename(lk->filename, result_file))
+ return -1;
+ lk->filename[0] = 0;
+ return 0;
+ }
+
+
+ /*On some error we need to rollback the changes we do this by removing .lock file*/
+ void rollback_lock_file(struct lock_file *lk)
+ {
+ if (lk->filename[0])
+ {
+ if (lk->fd >= 0)
+ {
+ /*cloase the file*/
+ close(lk->fd);
+ }
+
+ /*now remove the file*/
+ unlink(lk->filename);
+ }
+
+ lk->filename[0] = 0;
+ }
+
+
+ /*
+ * get the lines from a text file
+ */
+ static char **
+ readfile(const char *path)
+ {
+ FILE *infile;
+ int maxlength = 1,
+ linelen = 0;
+ int nlines = 0;
+ char **result;
+ char *buffer;
+ int c;
+
+ if ((infile = fopen(path, "r")) == NULL)
+ {
+ ereport(ERROR,
+ (errmsg("failed to read into to \"postgresql.auto.conf\" file")));
+ /*throw error*/
+ }
+
+ /* pass over the file twice - the first time to size the result */
+
+ while ((c = fgetc(infile)) != EOF)
+ {
+ linelen++;
+ if (c == '\n')
+ {
+ nlines++;
+ if (linelen > maxlength)
+ maxlength = linelen;
+ linelen = 0;
+ }
+ }
+
+ /* handle last line without a terminating newline (yuck) */
+ if (linelen)
+ nlines++;
+ if (linelen > maxlength)
+ maxlength = linelen;
+
+ /* set up the result and the line buffer */
+ result = (char **) palloc((nlines + 1) * sizeof(char *));
+ buffer = (char *) palloc(maxlength + 1);
+
+ /* now reprocess the file and store the lines */
+ rewind(infile);
+ nlines = 0;
+ while (fgets(buffer, maxlength + 1, infile) != NULL)
+ result[nlines++] = pstrdup(buffer);
+
+ fclose(infile);
+ pfree(buffer);
+ result[nlines] = NULL;
+
+ return result;
+ }
+
+ /*
+ * write an array of lines to a file
+ * used to write the in-memory of .auto.conf elements to .lock file
+ *
+ */
+ static void
+ writeAutoConffile(int fd, char **lines)
+ {
+ char **line;
+
+ for (line = lines; *line != NULL; line++)
+ {
+ if (write(fd, *line, strlen(*line)) < 0)
+ {
+ ereport(ERROR,
+ (errmsg("failed to write into to \"postgresql.auto.conf.lock\" file")));
+ /*throw error*/
+ }
+ pfree(*line);
+ }
+
+ if (pg_fsync(fd) != 0)
+ {
+ ereport(ERROR,
+ (errcode_for_file_access(),
+ errmsg("could not write to file \"postgresql.auto.conf.lock\": %m")));
+ }
+
+ }
+
+ /*
+ * make the changes requested by pg_change_conf() In-Memory by replacing the older content.
+ * If not already exist add the new (key,value) to end of list.
+ */
+ static char **
+ replace_autoline(char **lines,unsigned int repl_lineno , const char *replacement,
+ bool hasmatch)
+ {
+ int numlines = 1;
+ int i;
+ char **result;
+ int replen;
+ char *newline;
+
+ for (i = 0; lines[i]; i++)
+ {
+ numlines++;
+ }
+
+ if (!hasmatch)
+ {
+ /*key not found need to add one more line at the end*/
+ result = (char **) palloc0((numlines + 1)* sizeof(char *));
+ repl_lineno++;
+ }
+ else
+ {
+ result = (char **) palloc0(numlines * sizeof(char *));
+ }
+
+ replen = strlen(replacement);
+
+ for (i = 0; i < numlines; i++)
+ {
+ /* just copy pointer if NULL or no change needed */
+ if (lines[i] == NULL || (i != repl_lineno - 1))
+ {
+ result[i] = lines[i];
+ continue;
+ }
+
+ /* if we get here a change is needed - set up new line */
+
+ newline = (char *) palloc(replen + 1);
+
+ strcpy(newline, replacement);
+ result[i] = newline;
+ }
+
+ if (!hasmatch)
+ {
+ /*key not found in file add it to end of (key,value) list*/
+ newline = (char *) palloc(replen + 1);
+
+ strcpy(newline, replacement);
+ result[i - 1] = newline;
+ }
+
+ return result;
+ }
+
+
+ /*main function which edit .auto.file with given (key,value) input*/
+ void set_config_file(VariableSetStmt *setstmt)
+ {
+ struct strbuf sb;
+ char *key;
+ char *value;
+ char **conflines;
+ struct lock_file *lock = NULL;
+ int fd = -1, lineno = 0;
+ FILE *infile;
+ struct config_generic *record;
+ char abs_path[MAXPGPATH];
+ bool OK;
+ bool hasmatch;
+
+
+ if (IsTransactionBlock())
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_ACTIVE_SQL_TRANSACTION),
+ errmsg("cannot modify autoconf file inside a transaction")));
+ }
+
+ if (!superuser())
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ (errmsg("must be superuser to edit postgresql.conf"))));
+ }
+
+ /*1. validate the key and value and then make the conf info into (key, value, comment) pair*/
+
+ key = pstrdup(setstmt->name);
+ value = ExtractSetVariableArgs(setstmt);
+
+ record = validate_conf_option(key,value);
+
+ if (!record)
+ {
+ ereport(ERROR,
+ (errmsg("evaluation of config:values failed")));
+ }
+
+ sb.len = strlen(key);
+ sb.len += strlen(value);
+ sb.buf = palloc(sb.len + 100);
+
+ if (record->vartype == PGC_STRING)
+ {
+ snprintf(sb.buf, sb.len + 100, "%s = '%s' \n", key, value);
+ }
+ else
+ {
+ snprintf(sb.buf, sb.len + 100, "%s = %s \n", key, value);
+ }
+
+ /*2. create the lock file */
+
+ join_path_components(abs_path, data_directory, AutoConfigFileName);
+ canonicalize_path(abs_path);
+
+ PG_TRY();
+ {
+ lock = palloc0(sizeof(struct lock_file));
+
+ /*take the .lock file so other backends are restricted from access*/
+ fd = lock_confile(lock, abs_path, 0);
+ if (fd < 0)
+ {
+ ereport(ERROR,
+ (errmsg("lock file creation failed"))); }
+
+ /*open .auto.conf file*/
+ infile = fopen(abs_path, "r");
+ if (infile == NULL)
+ {
+ ereport(ERROR,
+ (errmsg("failed to open postgresql.auto.conf file")));
+ }
+
+
+ /*3. read from the auto.conf file and write new contents to .lock file */
+ conflines = readfile(abs_path);
+
+ OK = ParseAutoConfigFp(infile, abs_path, key, &lineno, -1, LOG, &hasmatch);
+ if (!OK)
+ {
+ ereport(ERROR,
+ (errmsg("parsing failed for postgresql.auto.conf file")));
+ /*throw error*/
+ }
+
+ fclose(infile);
+ conflines = replace_autoline(conflines, lineno - 1, sb.buf, hasmatch);
+
+ /*Write the New contents to .lock file*/
+ writeAutoConffile(fd, conflines);
+
+
+ /*4. commit the .lock file; here rename the lock file to .auto.conf file*/
+
+ if (commit_lock_file(lock) < 0)
+ {
+ ereport(ERROR,
+ (errmsg("commiting lock.postgresql.auto.conf failed")));
+ }
+ }
+ PG_CATCH()
+ {
+ if (lock)
+ {
+ /*remove the .lock file*/
+ rollback_lock_file(lock);
+ pfree(lock);
+ }
+
+ PG_RE_THROW();
+ }
+ PG_END_TRY();
+
+ pfree(lock);
+ return;
+ }
+
+
+
+
+ /*
* ALTER DATABASE name OWNER TO newowner
*/
void
*** a/src/backend/postmaster/postmaster.c
--- b/src/backend/postmaster/postmaster.c
***************
*** 507,512 **** PostmasterMain(int argc, char *argv[])
--- 507,513 ----
char *userDoption = NULL;
bool listen_addr_saved = false;
int i;
+ char conf_lock_file[MAXPGPATH];
MyProcPid = PostmasterPid = getpid();
***************
*** 1163,1168 **** PostmasterMain(int argc, char *argv[])
--- 1164,1176 ----
*/
RemovePgTempFiles();
+ /*Remove .auto.conf.lock file make be left over from last crash*/
+
+ /*get .auto.conf.lock file name */
+ strcpy(conf_lock_file, AutoConfigFileName);
+ strcat(conf_lock_file, CONF_LOCK_POSTFIX);
+ unlink(conf_lock_file);
+
/*
* Remember postmaster startup time
*/
*** a/src/backend/replication/basebackup.c
--- b/src/backend/replication/basebackup.c
***************
*** 581,586 **** sendDir(char *path, int basepathlen, bool sizeonly)
--- 581,592 ----
strlen(PG_TEMP_FILE_PREFIX)) == 0)
continue;
+ /*skip lock files used in postgresql.auto.conf edit*/
+ if (strncmp(de->d_name,
+ "postgresql.auto.conf.lock",
+ strlen("postgresql.auto.conf.lock")) == 0)
+ continue;
+
/*
* If there's a backup_label file, it belongs to a backup started by
* the user with pg_start_backup(). It is *not* correct for this
*** a/src/backend/tcop/utility.c
--- b/src/backend/tcop/utility.c
***************
*** 1061,1067 **** standard_ProcessUtility(Node *parsetree,
case T_AlterDatabaseSetStmt:
/* no event triggers for global objects */
AlterDatabaseSet((AlterDatabaseSetStmt *) parsetree);
! break;
case T_DropdbStmt:
{
--- 1061,1067 ----
case T_AlterDatabaseSetStmt:
/* no event triggers for global objects */
AlterDatabaseSet((AlterDatabaseSetStmt *) parsetree);
! break;
case T_DropdbStmt:
{
***************
*** 2061,2067 **** CreateCommandTag(Node *parsetree)
case T_AlterDatabaseSetStmt:
tag = "ALTER DATABASE";
break;
!
case T_DropdbStmt:
tag = "DROP DATABASE";
break;
--- 2061,2067 ----
case T_AlterDatabaseSetStmt:
tag = "ALTER DATABASE";
break;
!
case T_DropdbStmt:
tag = "DROP DATABASE";
break;
*** a/src/backend/utils/adt/misc.c
--- b/src/backend/utils/adt/misc.c
***************
*** 194,199 **** pg_reload_conf(PG_FUNCTION_ARGS)
--- 194,221 ----
/*
+ * pg_change_conf: function to write in conf file
+ *
+ */
+ Datum
+ pg_change_conf(PG_FUNCTION_ARGS)
+ {
+ char *key = PG_GETARG_CSTRING(0);
+ char *value = PG_GETARG_CSTRING(1);
+ VariableSetStmt setstmt;
+ A_Const *n = makeNode(A_Const);
+ n->val.type = T_String;
+ n->val.val.str = value;
+ n->location = -1;
+ setstmt.type = T_VariableSetStmt;
+ setstmt.kind = VAR_SET_VALUE;
+ setstmt.name = key;
+ setstmt.args = list_make1(n);
+ set_config_file(&setstmt);
+ PG_RETURN_BOOL(true);
+ }
+
+ /*
* Rotate log file
*/
Datum
*** a/src/backend/utils/misc/guc-file.l
--- b/src/backend/utils/misc/guc-file.l
***************
*** 468,473 **** GUC_flex_fatal(const char *msg)
--- 468,609 ----
return 0; /* keep compiler quiet */
}
+ /**
+ * Parse the config file and return the line number which matched the key(name)
+ **/
+
+ bool
+ ParseAutoConfigFp(FILE *fp, const char *config_file, char * name, int *line_no, int depth, int elevel,
+ bool *hasmatch)
+ {
+ volatile bool OK = true;
+ unsigned int save_ConfigFileLineno = ConfigFileLineno;
+ sigjmp_buf *save_GUC_flex_fatal_jmp = GUC_flex_fatal_jmp;
+ sigjmp_buf flex_fatal_jmp;
+ volatile YY_BUFFER_STATE lex_buffer = NULL;
+ int errorcount;
+ int token;
+
+ if (sigsetjmp(flex_fatal_jmp, 1) == 0)
+ GUC_flex_fatal_jmp = &flex_fatal_jmp;
+ else
+ {
+ /*
+ * Regain control after a fatal, internal flex error. It may have
+ * corrupted parser state. Consequently, abandon the file, but trust
+ * that the state remains sane enough for GUC_yy_delete_buffer().
+ */
+ elog(elevel, "%s at file \"%s\" line %u",
+ GUC_flex_fatal_errmsg, config_file, ConfigFileLineno);
+
+ OK = false;
+ goto cleanup;
+ }
+
+ /*
+ * Parse
+ */
+ ConfigFileLineno = 1;
+ errorcount = 0;
+ *hasmatch = false;
+
+ lex_buffer = GUC_yy_create_buffer(fp,YY_BUF_SIZE);
+ GUC_yy_switch_to_buffer(lex_buffer);
+
+ /* This loop iterates once per logical line */
+ while ((token = GUC_yylex()))
+ {
+ char *opt_name = NULL;
+
+ if (token == GUC_EOL) /* empty or comment line */
+ continue;
+
+ /* first token on line is option name */
+ if (token != GUC_ID && token != GUC_QUALIFIED_ID)
+ goto parse_error;
+ opt_name = pstrdup(GUC_yytext);
+
+ /* next we have an optional equal sign; discard if present */
+ token = GUC_yylex();
+ if (token == GUC_EQUALS)
+ token = GUC_yylex();
+
+ /* now we'd like an end of line, or possibly EOF */
+ token = GUC_yylex();
+ if (token != GUC_EOL)
+ {
+ if (token != 0)
+ goto parse_error;
+ /* treat EOF like \n for line numbering purposes, cf bug 4752 */
+ ConfigFileLineno++;
+ }
+
+ /* OK, process the option name and value */
+ if ((guc_name_compare(opt_name, name) == 0))
+ {
+ *hasmatch = true;
+ break;
+ }
+ else if (token == 0)
+ {
+ break;
+ }
+
+ continue;
+
+ parse_error:
+ /* release storage if we allocated any on this line */
+ if (opt_name)
+ pfree(opt_name);
+
+ /* report the error */
+ if (token == GUC_EOL || token == 0)
+ ereport(elevel,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("syntax error in file \"%s\" line %u, near end of line",
+ config_file, ConfigFileLineno - 1)));
+ else
+ ereport(elevel,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("syntax error in file \"%s\" line %u, near token \"%s\"",
+ config_file, ConfigFileLineno, GUC_yytext)));
+ OK = false;
+ errorcount++;
+
+ /*
+ * To avoid producing too much noise when fed a totally bogus file,
+ * give up after 100 syntax errors per file (an arbitrary number).
+ * Also, if we're only logging the errors at DEBUG level anyway,
+ * might as well give up immediately. (This prevents postmaster
+ * children from bloating the logs with duplicate complaints.)
+ */
+ if (errorcount >= 100 || elevel <= DEBUG1)
+ {
+ ereport(elevel,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("too many syntax errors found, abandoning file \"%s\"",
+ config_file)));
+ break;
+ }
+
+ /* resync to next end-of-line or EOF */
+ while (token != GUC_EOL && token != 0)
+ token = GUC_yylex();
+ /* break out of loop on EOF */
+ if (token == 0)
+ break;
+ }
+
+ cleanup:
+ GUC_yy_delete_buffer(lex_buffer);
+ /* Each recursion level must save and restore these static variables. */
+ *line_no = ConfigFileLineno;
+ ConfigFileLineno = save_ConfigFileLineno;
+ GUC_flex_fatal_jmp = save_GUC_flex_fatal_jmp;
+ return OK;
+ }
+
+
/*
* Read and parse a single configuration file. This function recurses
* to handle "include" directives.
*** a/src/backend/utils/misc/guc.c
--- b/src/backend/utils/misc/guc.c
***************
*** 5105,5110 **** config_enum_get_options(struct config_enum * record, const char *prefix,
--- 5105,5260 ----
return retstr.data;
}
+ /*given key and value validate the config value*/
+
+ struct config_generic *
+ validate_conf_option(const char *name, const char *value)
+ {
+ struct config_generic *record;
+
+ record = find_option(name, true, LOG);
+ if (record == NULL)
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("unrecognized configuration parameter \"%s\"", name)));
+ }
+
+ if (record->context == PGC_INTERNAL)
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_CANT_CHANGE_RUNTIME_PARAM),
+ errmsg("parameter \"%s\" cannot be changed",
+ name)));
+ }
+
+ /*
+ * Evaluate value and set variable.
+ */
+ switch (record->vartype)
+ {
+
+ case PGC_BOOL:
+ {
+ struct config_bool *conf = (struct config_bool *) record;
+ bool newval;
+ void *newextra = NULL;
+
+ if (value)
+ {
+ if (!parse_bool(value, &newval))
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("parameter \"%s\" requires a Boolean value",
+ name)));
+ }
+ if (!call_bool_check_hook(conf, &newval, &newextra,
+ PGC_S_FILE, LOG))
+ return NULL;
+ }
+ }
+ break;
+
+ case PGC_INT:
+ {
+ struct config_int *conf = (struct config_int *) record;
+ int newval;
+ void *newextra = NULL;
+
+ if (value)
+ {
+ const char *hintmsg;
+
+ if (!parse_int(value, &newval, conf->gen.flags, &hintmsg))
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("invalid value for parameter \"%s\": \"%s\"",
+ name, value),
+ hintmsg ? errhint("%s", _(hintmsg)) : 0));
+ }
+ if (newval < conf->min || newval > conf->max)
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("%d is outside the valid range for parameter \"%s\" (%d .. %d)",
+ newval, name, conf->min, conf->max)));
+ }
+ if (!call_int_check_hook(conf, &newval, &newextra,
+ PGC_S_FILE, LOG))
+ return NULL;
+ }
+ }
+
+ break;
+
+ case PGC_REAL:
+ {
+ struct config_real *conf = (struct config_real *) record;
+ double newval;
+ void *newextra = NULL;
+
+ if (value)
+ {
+ if (!parse_real(value, &newval))
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("parameter \"%s\" requires a numeric value",
+ name)));
+ }
+ if (newval < conf->min || newval > conf->max)
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("%g is outside the valid range for parameter \"%s\" (%g .. %g)",
+ newval, name, conf->min, conf->max)));
+ }
+ if (!call_real_check_hook(conf, &newval, &newextra,
+ PGC_S_FILE, LOG))
+ return NULL;
+ }
+ }
+
+ break;
+
+ case PGC_STRING:
+ {
+ struct config_string *conf = (struct config_string *) record;
+ char *newval;
+ void *newextra = NULL;
+
+ if (value)
+ {
+ /*
+ * The value passed by the caller could be transient, so
+ * we always strdup it.
+ */
+ newval = guc_strdup(LOG, value);
+ if (newval == NULL)
+ return NULL;
+
+ /*
+ * The only built-in "parsing" check we have is to apply
+ * truncation if GUC_IS_NAME.
+ */
+ if (conf->gen.flags & GUC_IS_NAME)
+ truncate_identifier(newval, strlen(newval), true);
+
+ if (!call_string_check_hook(conf, &newval, &newextra,
+ PGC_S_FILE, LOG))
+ {
+ free(newval);
+ return NULL;
+ }
+ }
+ }
+ }
+
+ return record;
+ }
+
/*
* Sets option `name' to given value.
*** a/src/backend/utils/misc/postgresql.conf.sample
--- b/src/backend/utils/misc/postgresql.conf.sample
***************
*** 30,36 ****
# h = hours
# d = days
-
#------------------------------------------------------------------------------
# FILE LOCATIONS
#------------------------------------------------------------------------------
--- 30,35 ----
*** a/src/bin/initdb/initdb.c
--- b/src/bin/initdb/initdb.c
***************
*** 177,182 **** static const char *backend_options = "--single -F -O -c search_path=pg_catalog -
--- 177,183 ----
static char bin_path[MAXPGPATH];
static char backend_exec[MAXPGPATH];
+ static char * make_absolute_path(const char *path);
static void *pg_malloc(size_t size);
static char *pg_strdup(const char *s);
static char **replace_token(char **lines,
***************
*** 1163,1168 **** setup_config(void)
--- 1164,1171 ----
char repltok[MAXPGPATH];
char path[MAXPGPATH];
const char *default_timezone;
+ char *abs_data_dir;
+ FILE *autofile;
fputs(_("creating configuration files ... "), stdout);
fflush(stdout);
***************
*** 1245,1255 **** setup_config(void)
--- 1248,1285 ----
conflines = replace_token(conflines, "#log_timezone = 'GMT'", repltok);
}
+ abs_data_dir = make_absolute_path(pg_data);
+
+ snprintf(repltok, sizeof(repltok), "include_dir = '%s/config_dir'",
+ abs_data_dir);
+ conflines = replace_token(conflines, "#include_dir = 'conf.d'", repltok);
+
snprintf(path, sizeof(path), "%s/postgresql.conf", pg_data);
writefile(path, conflines);
chmod(path, S_IRUSR | S_IWUSR);
+ /*create a .auto for conf file*/
+ sprintf(path, "%s/config_dir/postgresql.auto.conf", pg_data);
+ autofile = fopen(path, PG_BINARY_W);
+ if (autofile == NULL)
+ {
+ fprintf(stderr, _("%s: could not open file \"%s\" for writing: %s\n"),
+ progname, path, strerror(errno));
+ exit_nicely();
+ }
+
+
+ if (fclose(autofile))
+ {
+ fprintf(stderr, _("%s: could not write file \"%s\": %s\n"),
+ progname, path, strerror(errno));
+ exit_nicely();
+ }
+
+ chmod(path, S_IRUSR | S_IWUSR);
+
+
free(conflines);
***************
*** 2823,2828 **** check_need_password(const char *authmethodlocal, const char *authmethodhost)
--- 2853,2934 ----
}
}
+ /*
+ * If the given pathname isn't already absolute, make it so, interpreting
+ * it relative to the current working directory.
+ *
+ * Also canonicalizes the path. The result is always a malloc'd copy.
+ *
+ */
+ static char *
+ make_absolute_path(const char *path)
+ {
+ char *new;
+
+ /* Returning null for null input is convenient for some callers */
+ if (path == NULL)
+ return NULL;
+
+ if (!is_absolute_path(path))
+ {
+ char *buf;
+ size_t buflen;
+
+ buflen = MAXPGPATH;
+ for (;;)
+ {
+ buf = malloc(buflen);
+ if (!buf)
+ {
+ fprintf(stderr, _("%s: Memory allocation failed\n"),
+ progname);
+ exit_nicely();
+ }
+
+ if (getcwd(buf, buflen))
+ break;
+ else if (errno == ERANGE)
+ {
+ free(buf);
+ buflen *= 2;
+ continue;
+ }
+ else
+ {
+ free(buf);
+ fprintf(stderr, _("%s: could not get current working directory\n"),
+ progname);
+ exit_nicely();
+ }
+ }
+
+ new = malloc(strlen(buf) + strlen(path) + 2);
+ if (!new)
+ {
+ fprintf(stderr, _("%s: Memory allocation failed\n"),
+ progname);
+ exit_nicely();
+ }
+ sprintf(new, "%s/%s", buf, path);
+ free(buf);
+ }
+ else
+ {
+ new = strdup(path);
+ if (!new)
+ {
+ fprintf(stderr, _("%s: Memory allocation failed\n"),
+ progname);
+ exit_nicely();
+ }
+ }
+
+ /* Make sure punctuation is canonical, too */
+ canonicalize_path(new);
+
+ return new;
+ }
+
int
main(int argc, char *argv[])
{
***************
*** 2887,2893 **** main(int argc, char *argv[])
"base",
"base/1",
"pg_tblspc",
! "pg_stat_tmp"
};
progname = get_progname(argv[0]);
--- 2993,3000 ----
"base",
"base/1",
"pg_tblspc",
! "pg_stat_tmp",
! "config_dir"
};
progname = get_progname(argv[0]);
*** a/src/include/access/xlog_internal.h
--- b/src/include/access/xlog_internal.h
***************
*** 255,260 **** extern void XLogArchiveCleanup(const char *xlog);
--- 255,261 ----
/*
* These aren't in xlog.h because I'd rather not include fmgr.h there.
*/
+
extern Datum pg_start_backup(PG_FUNCTION_ARGS);
extern Datum pg_stop_backup(PG_FUNCTION_ARGS);
extern Datum pg_switch_xlog(PG_FUNCTION_ARGS);
*** a/src/include/catalog/pg_proc.h
--- b/src/include/catalog/pg_proc.h
***************
*** 3005,3010 **** DATA(insert OID = 2625 ( pg_ls_dir PGNSP PGUID 12 1 1000 0 0 f f f f t t v 1 0
--- 3005,3012 ----
DESCR("list all files in a directory");
DATA(insert OID = 2626 ( pg_sleep PGNSP PGUID 12 1 0 0 0 f f f f t f v 1 0 2278 "701" _null_ _null_ _null_ _null_ pg_sleep _null_ _null_ _null_ ));
DESCR("sleep for the specified time in seconds");
+ DATA(insert OID = 2634 ( pg_change_conf PGNSP PGUID 12 1 0 0 0 f f f f t f v 2 0 16 "2275 2275" _null_ _null_ _null_ _null_ pg_change_conf _null_ _null_ _null_ ));
+ DESCR("changes a parameter in .auto.conf file");
DATA(insert OID = 2971 ( text PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 25 "16" _null_ _null_ _null_ _null_ booltext _null_ _null_ _null_ ));
DESCR("convert boolean to text");
*** a/src/include/commands/dbcommands.h
--- b/src/include/commands/dbcommands.h
***************
*** 58,63 **** extern void RenameDatabase(const char *oldname, const char *newname);
--- 58,64 ----
extern void AlterDatabase(AlterDatabaseStmt *stmt, bool isTopLevel);
extern void AlterDatabaseSet(AlterDatabaseSetStmt *stmt);
extern void AlterDatabaseOwner(const char *dbname, Oid newOwnerId);
+ extern void set_config_file(VariableSetStmt *setstmt);
extern Oid get_database_oid(const char *dbname, bool missingok);
extern char *get_database_name(Oid dbid);
*** a/src/include/nodes/parsenodes.h
--- b/src/include/nodes/parsenodes.h
***************
*** 2361,2366 **** typedef struct AlterDatabaseSetStmt
--- 2361,2368 ----
VariableSetStmt *setstmt; /* SET or RESET subcommand */
} AlterDatabaseSetStmt;
+
+
/* ----------------------
* Dropdb Statement
* ----------------------
*** a/src/include/utils/builtins.h
--- b/src/include/utils/builtins.h
***************
*** 482,487 **** extern Datum pg_sleep(PG_FUNCTION_ARGS);
--- 482,488 ----
extern Datum pg_get_keywords(PG_FUNCTION_ARGS);
extern Datum pg_typeof(PG_FUNCTION_ARGS);
extern Datum pg_collation_for(PG_FUNCTION_ARGS);
+ extern Datum pg_change_conf(PG_FUNCTION_ARGS);
/* oid.c */
extern Datum oidin(PG_FUNCTION_ARGS);
*** a/src/include/utils/guc.h
--- b/src/include/utils/guc.h
***************
*** 191,196 **** typedef enum
--- 191,201 ----
#define GUC_NOT_WHILE_SEC_REST 0x8000 /* can't set if security restricted */
+ /*postgresql.auto.file name and its lock*/
+ #define AutoConfigFileName "config_dir/postgresql.auto.conf"
+ #define CONF_LOCK_POSTFIX ".lock"
+
+
/* GUC vars that are actually declared in guc.c, rather than elsewhere */
extern bool log_duration;
extern bool Debug_print_plan;
***************
*** 329,334 **** extern void SetPGVariable(const char *name, List *args, bool is_local);
--- 334,343 ----
extern void GetPGVariable(const char *name, DestReceiver *dest);
extern TupleDesc GetPGVariableResultDesc(const char *name);
+ extern bool
+ ParseAutoConfigFp(FILE *fp, const char *config_file, char * name, int *line_no, int depth, int elevel,
+ bool *hasmatch);
+
extern void ExecSetVariableStmt(VariableSetStmt *stmt);
extern char *ExtractSetVariableArgs(VariableSetStmt *stmt);
*** a/src/include/utils/guc_tables.h
--- b/src/include/utils/guc_tables.h
***************
*** 260,265 **** extern void build_guc_variables(void);
--- 260,267 ----
extern const char *config_enum_lookup_by_value(struct config_enum * record, int val);
extern bool config_enum_lookup_by_name(struct config_enum * record,
const char *value, int *retval);
+ extern struct config_generic *
+ validate_conf_option(const char *name, const char *value);
#endif /* GUC_TABLES_H */