set_persistent.v1.patch
application/octet-stream
Filename: set_persistent.v1.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
Series: patch v1
| File | + | − |
|---|---|---|
| src/backend/access/transam/xact.c | 3 | 0 |
| src/backend/commands/dbcommands.c | 339 | 0 |
| src/backend/parser/gram.y | 12 | 0 |
| src/backend/postmaster/postmaster.c | 6 | 0 |
| src/backend/replication/basebackup.c | 6 | 0 |
| src/backend/tcop/utility.c | 8 | 0 |
| src/backend/utils/misc/guc.c | 150 | 0 |
| src/backend/utils/misc/guc-file.l | 5 | 0 |
| src/bin/initdb/initdb.c | 107 | 0 |
| src/include/commands/dbcommands.h | 3 | 0 |
| src/include/nodes/parsenodes.h | 1 | 0 |
| src/include/parser/kwlist.h | 1 | 0 |
| src/include/utils/guc.h | 5 | 0 |
| src/include/utils/guc_tables.h | 2 | 0 |
*** a/src/backend/access/transam/xact.c
--- b/src/backend/access/transam/xact.c
***************
*** 1844,1849 **** CommitTransaction(void)
--- 1844,1851 ----
/* close large objects before lower-level cleanup */
AtEOXact_LargeObject(true);
+ AtEOXact_UpdateAutoConfFiles(true);
+
/*
* Mark serializable transaction as complete for predicate locking
* purposes. This should be done as late as we can put it and still allow
***************
*** 2302,2307 **** AbortTransaction(void)
--- 2304,2310 ----
AtAbort_Portals();
AtEOXact_LargeObject(false);
AtAbort_Notify();
+ AtEOXact_UpdateAutoConfFiles(false);
AtEOXact_RelationMap(false);
/*
*** a/src/backend/commands/dbcommands.c
--- b/src/backend/commands/dbcommands.c
***************
*** 58,65 ****
--- 58,74 ----
#include "utils/snapmgr.h"
#include "utils/syscache.h"
#include "utils/tqual.h"
+ #include "utils/guc_tables.h"
+ #include "utils/memutils.h"
+ /* lock file used in edit postgresql.auto.conf */
+ struct lock_file
+ {
+ int fd;
+ char filename[MAXPGPATH];
+ };
+
typedef struct
{
Oid src_dboid; /* source (template) DB */
***************
*** 72,77 **** typedef struct
--- 81,88 ----
Oid dest_tsoid; /* tablespace we are trying to move to */
} movedb_failure_params;
+ static struct lock_file *lockConfFile = NULL;
+
/* non-export function prototypes */
static void createdb_failure_callback(int code, Datum arg);
static void movedb(const char *dbname, const char *tblspcname);
***************
*** 86,91 **** static void remove_dbtablespaces(Oid db_id);
--- 97,108 ----
static bool check_db_file_conflict(Oid db_id);
static int errdetail_busy_db(int notherbackends, int npreparedxacts);
+ static int commit_lock_file(struct lock_file * lk);
+ static void rollback_lock_file(struct lock_file * lk);
+ static int close_lock_file(struct lock_file * lk);
+ static void writeAutoConffile(int fd, ConfigVariable **head_p, char *config_file);
+ static void replace_autoline(ConfigVariable **head_p, ConfigVariable **tail_p,
+ char *config_file, char *name, char *value, bool value_is_string);
/*
* CREATE DATABASE
***************
*** 1434,1439 **** AlterDatabaseSet(AlterDatabaseSetStmt *stmt)
--- 1451,1778 ----
/*
+ * create a postgres.auto.conf.lock file for postgres.auto.conf
+ * file so that it act as an lock to prevent concurrent edit on
+ * postgres.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, 0600);
+ 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 postgres.auto.conf.lock file */
+ int
+ close_lock_file(struct lock_file * lk)
+ {
+ int fd = lk->fd;
+
+ lk->fd = -1;
+ return close(fd);
+ }
+
+
+ /*
+ * On commit, rename postgres.auto.conf.lock to postgres.auto.conf
+ * As postgres.auto.conf.lock contains updated parameter values.
+ */
+ 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 rollback, Delete the postgres.auto.conf.lock file, As we did not
+ * modifed anything in postgres.auto.conf
+ */
+ 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;
+ }
+
+
+ /* Write updated configuration parameter values into postgres.auto.conf.lock */
+ static void
+ writeAutoConffile(int fd, ConfigVariable **head_p, char *config_file)
+ {
+ ConfigVariable *item;
+ StringInfoData buf;
+
+ initStringInfo(&buf);
+
+ for (item = *head_p; item != NULL; item = item->next)
+ {
+ appendStringInfoString(&buf, item->name);
+ appendStringInfoString(&buf, " = ");
+
+ if (true == item->value_is_string)
+ {
+ appendStringInfoString(&buf, "\'");
+ appendStringInfoString(&buf, item->value);
+ appendStringInfoString(&buf, "\'");
+ }
+ else
+ {
+ appendStringInfoString(&buf, item->value);
+ }
+
+ appendStringInfoString(&buf, "\n");
+
+ if (write(fd, buf.data, buf.len) < 0)
+ {
+ ereport(ERROR,
+ (errmsg("failed to write into to \"postgresql.auto.conf.lock\" file")));
+ }
+
+ resetStringInfo(&buf);
+ }
+
+ pfree(buf.data);
+ return;
+ }
+
+
+ /*
+ * Replace the updated configuration parameter value in postgres.auto.conf
+ * file lines. If it is new then append extra line at the end of lines.
+ */
+ static void
+ replace_autoline(ConfigVariable **head_p, ConfigVariable **tail_p,
+ char *config_file, char *name, char *value,
+ bool value_is_string)
+ {
+
+ ConfigVariable *item;
+
+ if (NULL != *head_p)
+ {
+ for (item = *head_p; item != NULL; item = item->next)
+ {
+ if (strcmp(item->name, name) == 0)
+ {
+ pfree(item->value);
+ item->value = pstrdup(value);
+ return;
+ }
+ }
+ }
+
+ item = palloc(sizeof *item);
+ item->name = pstrdup(name);
+ item->value = pstrdup(value);
+ item->value_is_string = value_is_string;
+ item->filename = pstrdup(config_file);
+ item->next = NULL;
+
+ if (*head_p == NULL)
+ {
+ item->sourceline = 1;
+ *head_p = item;
+ }
+ else
+ {
+ item->sourceline = (*tail_p)->sourceline+1;
+ (*tail_p)->next = item;
+ }
+
+ *tail_p = item;
+
+ return;
+ }
+
+
+ /*
+ * Edit postgres.conf parameters WITH SQL commands "SET PERSISTENT"
+ * by adding new postgres.auto.conf file in include configuration directory
+ * which will be processed at the end of postgres.conf file
+ */
+ void
+ set_config_file(VariableSetStmt *setstmt)
+ {
+ char *name;
+ char *value;
+ int fd = -1;
+ FILE *infile;
+ struct config_generic *record;
+ char abs_path[MAXPGPATH];
+ ConfigVariable *head_p = NULL;
+ ConfigVariable *tail_p = NULL;
+
+ /*
+ * This command is modifing the configuration file so no need of
+ * transaction properties, so not allowed inside a transaction.
+ */
+ if (IsTransactionBlock())
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_ACTIVE_SQL_TRANSACTION),
+ errmsg("SET PERSISTENT command is not allowed inside a transaction")));
+ }
+
+ if (!superuser())
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ (errmsg("must be superuser to execute SET PERSISTENT command"))));
+ }
+
+ /*
+ * Validate the name and arguments [value1, value2 ... ].
+ */
+
+ name = pstrdup(setstmt->name);
+ value = ExtractSetVariableArgs(setstmt);
+
+ record = validate_conf_option(name, value);
+ if (!record)
+ ereport(ERROR,
+ (errmsg("invalid value for parameter \"%s\": \"%s\"", name, value)));
+
+ /* Create the postgres.auto.conf.lock file */
+ join_path_components(abs_path, data_directory, AutoConfigFileName);
+ canonicalize_path(abs_path);
+
+ lockConfFile = MemoryContextAllocZero(TopTransactionContext, sizeof(struct lock_file));
+
+ fd = lock_confile(lockConfFile, abs_path, 0);
+ if (fd < 0)
+ {
+ pfree(lockConfFile);
+ lockConfFile = NULL;
+ ereport(ERROR,
+ (errmsg("\"postgresql.auto.conf.lock\" file creation failed")));
+ }
+
+ /* open postgres.auto.conf file */
+ infile = AllocateFile(abs_path, "r");
+ if (infile == NULL)
+ {
+ ereport(ERROR,
+ (errmsg("failed to open \"postgresql.auto.conf\" file")));
+ }
+
+ /* Parse the postgres.auto.conf file */
+ ParseConfigFp(infile, abs_path, -1, LOG, &head_p, &tail_p);
+
+ FreeFile(infile);
+
+ /* replace with new value */
+ replace_autoline(&head_p, &tail_p, abs_path, name, value,
+ (record->vartype == PGC_STRING) ? true : false);
+
+ /* Write the New contents to postgres.auto.conf.lock file */
+ writeAutoConffile(fd, &head_p, abs_path);
+
+ /*
+ * On Commit, rename postgres.auto.conf.lock to postgres.auto.conf
+ * and On Rollback, delete the postgres.auto.conf.lock
+ */
+
+ return;
+ }
+
+ /*
+ * This routine is called during transaction commit or abort.
+ *
+ * On commit, renames the .auto.conf.lock to .auto.conf, for updateding changes
+ * into .auto.conf file
+ *
+ * On abort, just delete the .auto.conf.lock file.
+ *
+ * NB: this should be the last step before actual transaction commit.
+ * If any error aborts the transaction after we run this code, the postmaster
+ * will still have received and cached the changed data; so minimize the
+ * window for such problems.
+ */
+ void
+ AtEOXact_UpdateAutoConfFiles(bool isCommit)
+ {
+ if (lockConfFile)
+ {
+ if (isCommit)
+ {
+ if (commit_lock_file(lockConfFile) < 0)
+ ereport(ERROR,
+ (errmsg("commiting chages to \"postgresql.auto.conf\" failed")));
+ }
+ else
+ {
+ rollback_lock_file(lockConfFile);
+ }
+
+ pfree(lockConfFile);
+ lockConfFile = NULL;
+ }
+ }
+
+ /*
* ALTER DATABASE name OWNER TO newowner
*/
void
*** a/src/backend/parser/gram.y
--- b/src/backend/parser/gram.y
***************
*** 564,571 **** static void processCASbits(int cas_bits, int location, const char *constrType,
OBJECT_P OF OFF OFFSET OIDS ON ONLY OPERATOR OPTION OPTIONS OR
ORDER OUT_P OUTER_P OVER OVERLAPS OVERLAY OWNED OWNER
! PARSER PARTIAL PARTITION PASSING PASSWORD PLACING PLANS POSITION
! PRECEDING PRECISION PRESERVE PREPARE PREPARED PRIMARY
PRIOR PRIVILEGES PROCEDURAL PROCEDURE
QUOTE
--- 564,571 ----
OBJECT_P OF OFF OFFSET OIDS ON ONLY OPERATOR OPTION OPTIONS OR
ORDER OUT_P OUTER_P OVER OVERLAPS OVERLAY OWNED OWNER
! PARSER PARTIAL PARTITION PASSING PASSWORD PERSISTENT PLACING PLANS
! POSITION PRECEDING PRECISION PRESERVE PREPARE PREPARED PRIMARY
PRIOR PRIVILEGES PROCEDURAL PROCEDURE
QUOTE
***************
*** 1293,1298 **** VariableSetStmt:
--- 1293,1307 ----
n->is_local = false;
$$ = (Node *) n;
}
+ | SET PERSISTENT var_name '=' var_list
+ {
+ VariableSetStmt *n = makeNode(VariableSetStmt);
+ n->kind = VAR_SET_VALUE;
+ n->name = $3;
+ n->args = $5;
+ n->is_persistent = true;
+ $$ = (Node *)n;
+ }
;
set_rest:
***************
*** 12586,12591 **** unreserved_keyword:
--- 12595,12601 ----
| PARTITION
| PASSING
| PASSWORD
+ | PERSISTENT
| PLANS
| PRECEDING
| PREPARE
*** 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,1174 ----
*/
RemovePgTempFiles();
+ /* Remove postgres.auto.conf.lock file make be left over from last crash */
+ 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
***************
*** 1145,1151 **** standard_ProcessUtility(Node *parsetree,
break;
case T_VariableSetStmt:
! ExecSetVariableStmt((VariableSetStmt *) parsetree);
break;
case T_VariableShowStmt:
--- 1145,1158 ----
break;
case T_VariableSetStmt:
! if (((VariableSetStmt *) parsetree)->is_persistent == true)
! {
! set_config_file((VariableSetStmt *) parsetree);
! }
! else
! {
! ExecSetVariableStmt((VariableSetStmt *) parsetree);
! }
break;
case T_VariableShowStmt:
*** a/src/backend/utils/misc/guc-file.l
--- b/src/backend/utils/misc/guc-file.l
***************
*** 533,538 **** ParseConfigFp(FILE *fp, const char *config_file, int depth, int elevel,
--- 533,539 ----
{
char *opt_name = NULL;
char *opt_value = NULL;
+ bool is_string = false;
ConfigVariable *item;
if (token == GUC_EOL) /* empty or comment line */
***************
*** 556,562 **** ParseConfigFp(FILE *fp, const char *config_file, int depth, int elevel,
--- 557,566 ----
token != GUC_UNQUOTED_STRING)
goto parse_error;
if (token == GUC_STRING) /* strip quotes and escapes */
+ {
opt_value = GUC_scanstr(yytext);
+ is_string = true;
+ }
else
opt_value = pstrdup(yytext);
***************
*** 620,625 **** ParseConfigFp(FILE *fp, const char *config_file, int depth, int elevel,
--- 624,630 ----
item = palloc(sizeof *item);
item->name = opt_name;
item->value = opt_value;
+ item->value_is_string = is_string;
item->filename = pstrdup(config_file);
item->sourceline = ConfigFileLineno-1;
item->next = NULL;
*** 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/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];
+ 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,1284 ----
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)
--- 2852,2933 ----
}
}
+ /*
+ * 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.
+ *
+ */
+ 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]);
--- 2992,2999 ----
"base",
"base/1",
"pg_tblspc",
! "pg_stat_tmp",
! "config_dir"
};
progname = get_progname(argv[0]);
*** 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);
***************
*** 67,70 **** extern void dbase_desc(StringInfo buf, uint8 xl_info, char *rec);
--- 68,73 ----
extern void check_encoding_locale_matches(int encoding, const char *collate, const char *ctype);
+ extern void AtEOXact_UpdateAutoConfFiles(bool isCommit);
+
#endif /* DBCOMMANDS_H */
*** a/src/include/nodes/parsenodes.h
--- b/src/include/nodes/parsenodes.h
***************
*** 1425,1430 **** typedef struct VariableSetStmt
--- 1425,1431 ----
char *name; /* variable to be set */
List *args; /* List of A_Const nodes */
bool is_local; /* SET LOCAL? */
+ bool is_persistent; /* SET PERSISTENT? */
} VariableSetStmt;
/* ----------------------
*** a/src/include/parser/kwlist.h
--- b/src/include/parser/kwlist.h
***************
*** 279,284 **** PG_KEYWORD("partial", PARTIAL, UNRESERVED_KEYWORD)
--- 279,285 ----
PG_KEYWORD("partition", PARTITION, UNRESERVED_KEYWORD)
PG_KEYWORD("passing", PASSING, UNRESERVED_KEYWORD)
PG_KEYWORD("password", PASSWORD, UNRESERVED_KEYWORD)
+ PG_KEYWORD("persistent", PERSISTENT, UNRESERVED_KEYWORD)
PG_KEYWORD("placing", PLACING, RESERVED_KEYWORD)
PG_KEYWORD("plans", PLANS, UNRESERVED_KEYWORD)
PG_KEYWORD("position", POSITION, COL_NAME_KEYWORD)
*** a/src/include/utils/guc.h
--- b/src/include/utils/guc.h
***************
*** 105,110 **** typedef struct ConfigVariable
--- 105,111 ----
{
char *name;
char *value;
+ int value_is_string;
char *filename;
int sourceline;
struct ConfigVariable *next;
***************
*** 191,196 **** typedef enum
--- 192,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;
*** 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 */