*** 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 */