mirror of
https://github.com/postgres/postgres.git
synced 2025-07-12 21:01:52 +03:00
Add ALTER SYSTEM command to edit the server configuration file.
Patch contributed by Amit Kapila. Reviewed by Hari Babu, Masao Fujii, Boszormenyi Zoltan, Andres Freund, Greg Smith and others.
This commit is contained in:
@ -207,6 +207,10 @@ static char *config_enum_get_options(struct config_enum * record,
|
||||
const char *prefix, const char *suffix,
|
||||
const char *separator);
|
||||
|
||||
static bool validate_conf_option(struct config_generic * record,
|
||||
const char *name, const char *value, GucSource source,
|
||||
int elevel, bool freemem, void *newval, void **newextra);
|
||||
|
||||
|
||||
/*
|
||||
* Options for enum values defined in this module.
|
||||
@ -3484,6 +3488,9 @@ static void ShowAllGUCConfig(DestReceiver *dest);
|
||||
static char *_ShowOption(struct config_generic * record, bool use_units);
|
||||
static bool validate_option_array_item(const char *name, const char *value,
|
||||
bool skipIfNoPermissions);
|
||||
static void write_auto_conf_file(int fd, const char *filename, ConfigVariable **head_p);
|
||||
static void replace_auto_config_value(ConfigVariable **head_p, ConfigVariable **tail_p,
|
||||
char *config_file, char *name, char *value);
|
||||
|
||||
|
||||
/*
|
||||
@ -5248,6 +5255,220 @@ config_enum_get_options(struct config_enum * record, const char *prefix,
|
||||
return retstr.data;
|
||||
}
|
||||
|
||||
/*
|
||||
* Validates configuration parameter and value, by calling check hook functions
|
||||
* depending on record's vartype. It validates if the parameter
|
||||
* value given is in range of expected predefined value for that parameter.
|
||||
*
|
||||
* freemem - true indicates memory for newval and newextra will be
|
||||
* freed in this function, false indicates it will be freed
|
||||
* by caller.
|
||||
* Return value:
|
||||
* 1: the value is valid
|
||||
* 0: the name or value is invalid
|
||||
*/
|
||||
bool
|
||||
validate_conf_option(struct config_generic * record, const char *name,
|
||||
const char *value, GucSource source, int elevel,
|
||||
bool freemem, void *newval, void **newextra)
|
||||
{
|
||||
/*
|
||||
* Validate the value for the passed record, to ensure it is in expected
|
||||
* range.
|
||||
*/
|
||||
switch (record->vartype)
|
||||
{
|
||||
|
||||
case PGC_BOOL:
|
||||
{
|
||||
struct config_bool *conf = (struct config_bool *) record;
|
||||
bool tmpnewval;
|
||||
|
||||
if (newval == NULL)
|
||||
newval = &tmpnewval;
|
||||
|
||||
if (value != NULL)
|
||||
{
|
||||
if (!parse_bool(value, newval))
|
||||
{
|
||||
ereport(elevel,
|
||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||
errmsg("parameter \"%s\" requires a Boolean value",
|
||||
name)));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!call_bool_check_hook(conf, newval, newextra,
|
||||
source, elevel))
|
||||
return 0;
|
||||
|
||||
if (*newextra && freemem)
|
||||
free(*newextra);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case PGC_INT:
|
||||
{
|
||||
struct config_int *conf = (struct config_int *) record;
|
||||
int tmpnewval;
|
||||
|
||||
if (newval == NULL)
|
||||
newval = &tmpnewval;
|
||||
|
||||
if (value != NULL)
|
||||
{
|
||||
const char *hintmsg;
|
||||
|
||||
if (!parse_int(value, newval, conf->gen.flags, &hintmsg))
|
||||
{
|
||||
ereport(elevel,
|
||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||
errmsg("invalid value for parameter \"%s\": \"%s\"",
|
||||
name, value),
|
||||
hintmsg ? errhint("%s", _(hintmsg)) : 0));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (*((int *) newval) < conf->min || *((int *) newval) > conf->max)
|
||||
{
|
||||
ereport(elevel,
|
||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||
errmsg("%d is outside the valid range for parameter \"%s\" (%d .. %d)",
|
||||
*((int *) newval), name, conf->min, conf->max)));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!call_int_check_hook(conf, newval, newextra,
|
||||
source, elevel))
|
||||
return 0;
|
||||
|
||||
if (*newextra && freemem)
|
||||
free(*newextra);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case PGC_REAL:
|
||||
{
|
||||
struct config_real *conf = (struct config_real *) record;
|
||||
double tmpnewval;
|
||||
|
||||
if (newval == NULL)
|
||||
newval = &tmpnewval;
|
||||
|
||||
if (value != NULL)
|
||||
{
|
||||
if (!parse_real(value, newval))
|
||||
{
|
||||
ereport(elevel,
|
||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||
errmsg("parameter \"%s\" requires a numeric value",
|
||||
name)));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (*((double *) newval) < conf->min || *((double *) newval) > conf->max)
|
||||
{
|
||||
ereport(elevel,
|
||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||
errmsg("%g is outside the valid range for parameter \"%s\" (%g .. %g)",
|
||||
*((double *) newval), name, conf->min, conf->max)));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!call_real_check_hook(conf, newval, newextra,
|
||||
source, elevel))
|
||||
return 0;
|
||||
|
||||
if (*newextra && freemem)
|
||||
free(*newextra);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case PGC_STRING:
|
||||
{
|
||||
struct config_string *conf = (struct config_string *) record;
|
||||
char *tempPtr;
|
||||
char **tmpnewval = newval;
|
||||
|
||||
if (newval == NULL)
|
||||
tmpnewval = &tempPtr;
|
||||
|
||||
if (value != NULL)
|
||||
{
|
||||
/*
|
||||
* The value passed by the caller could be transient, so
|
||||
* we always strdup it.
|
||||
*/
|
||||
*tmpnewval = guc_strdup(elevel, value);
|
||||
if (*tmpnewval == NULL)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* 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(*tmpnewval, strlen(*tmpnewval), true);
|
||||
|
||||
if (!call_string_check_hook(conf, tmpnewval, newextra,
|
||||
source, elevel))
|
||||
{
|
||||
free(*tmpnewval);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Free the malloc'd data if any */
|
||||
if (freemem)
|
||||
{
|
||||
if (*tmpnewval != NULL)
|
||||
free(*tmpnewval);
|
||||
if (*newextra != NULL)
|
||||
free(*newextra);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case PGC_ENUM:
|
||||
{
|
||||
struct config_enum *conf = (struct config_enum *) record;
|
||||
int tmpnewval;
|
||||
|
||||
if (newval == NULL)
|
||||
newval = &tmpnewval;
|
||||
|
||||
if (value != NULL)
|
||||
{
|
||||
if (!config_enum_lookup_by_name(conf, value, newval))
|
||||
{
|
||||
char *hintmsg;
|
||||
|
||||
hintmsg = config_enum_get_options(conf,
|
||||
"Available values: ",
|
||||
".", ", ");
|
||||
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||
errmsg("invalid value for parameter \"%s\": \"%s\"",
|
||||
name, value),
|
||||
hintmsg ? errhint("%s", _(hintmsg)) : 0));
|
||||
|
||||
if (hintmsg != NULL)
|
||||
pfree(hintmsg);
|
||||
return 0;
|
||||
}
|
||||
if (!call_enum_check_hook(conf, newval, newextra,
|
||||
source, LOG))
|
||||
return 0;
|
||||
|
||||
if (*newextra && freemem)
|
||||
free(*newextra);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Sets option `name' to given value.
|
||||
@ -5496,16 +5717,9 @@ set_config_option(const char *name, const char *value,
|
||||
|
||||
if (value)
|
||||
{
|
||||
if (!parse_bool(value, &newval))
|
||||
{
|
||||
ereport(elevel,
|
||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||
errmsg("parameter \"%s\" requires a Boolean value",
|
||||
name)));
|
||||
return 0;
|
||||
}
|
||||
if (!call_bool_check_hook(conf, &newval, &newextra,
|
||||
source, elevel))
|
||||
if (!validate_conf_option(record, name, value, source,
|
||||
elevel, false, &newval,
|
||||
&newextra))
|
||||
return 0;
|
||||
}
|
||||
else if (source == PGC_S_DEFAULT)
|
||||
@ -5589,27 +5803,9 @@ set_config_option(const char *name, const char *value,
|
||||
|
||||
if (value)
|
||||
{
|
||||
const char *hintmsg;
|
||||
|
||||
if (!parse_int(value, &newval, conf->gen.flags, &hintmsg))
|
||||
{
|
||||
ereport(elevel,
|
||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||
errmsg("invalid value for parameter \"%s\": \"%s\"",
|
||||
name, value),
|
||||
hintmsg ? errhint("%s", _(hintmsg)) : 0));
|
||||
return 0;
|
||||
}
|
||||
if (newval < conf->min || newval > conf->max)
|
||||
{
|
||||
ereport(elevel,
|
||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||
errmsg("%d is outside the valid range for parameter \"%s\" (%d .. %d)",
|
||||
newval, name, conf->min, conf->max)));
|
||||
return 0;
|
||||
}
|
||||
if (!call_int_check_hook(conf, &newval, &newextra,
|
||||
source, elevel))
|
||||
if (!validate_conf_option(record, name, value, source,
|
||||
elevel, false, &newval,
|
||||
&newextra))
|
||||
return 0;
|
||||
}
|
||||
else if (source == PGC_S_DEFAULT)
|
||||
@ -5693,24 +5889,9 @@ set_config_option(const char *name, const char *value,
|
||||
|
||||
if (value)
|
||||
{
|
||||
if (!parse_real(value, &newval))
|
||||
{
|
||||
ereport(elevel,
|
||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||
errmsg("parameter \"%s\" requires a numeric value",
|
||||
name)));
|
||||
return 0;
|
||||
}
|
||||
if (newval < conf->min || newval > conf->max)
|
||||
{
|
||||
ereport(elevel,
|
||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||
errmsg("%g is outside the valid range for parameter \"%s\" (%g .. %g)",
|
||||
newval, name, conf->min, conf->max)));
|
||||
return 0;
|
||||
}
|
||||
if (!call_real_check_hook(conf, &newval, &newextra,
|
||||
source, elevel))
|
||||
if (!validate_conf_option(record, name, value, source,
|
||||
elevel, false, &newval,
|
||||
&newextra))
|
||||
return 0;
|
||||
}
|
||||
else if (source == PGC_S_DEFAULT)
|
||||
@ -5794,27 +5975,10 @@ set_config_option(const char *name, const char *value,
|
||||
|
||||
if (value)
|
||||
{
|
||||
/*
|
||||
* The value passed by the caller could be transient, so
|
||||
* we always strdup it.
|
||||
*/
|
||||
newval = guc_strdup(elevel, value);
|
||||
if (newval == NULL)
|
||||
if (!validate_conf_option(record, name, value, source,
|
||||
elevel, false, &newval,
|
||||
&newextra))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* 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,
|
||||
source, elevel))
|
||||
{
|
||||
free(newval);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else if (source == PGC_S_DEFAULT)
|
||||
{
|
||||
@ -5920,26 +6084,9 @@ set_config_option(const char *name, const char *value,
|
||||
|
||||
if (value)
|
||||
{
|
||||
if (!config_enum_lookup_by_name(conf, value, &newval))
|
||||
{
|
||||
char *hintmsg;
|
||||
|
||||
hintmsg = config_enum_get_options(conf,
|
||||
"Available values: ",
|
||||
".", ", ");
|
||||
|
||||
ereport(elevel,
|
||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||
errmsg("invalid value for parameter \"%s\": \"%s\"",
|
||||
name, value),
|
||||
hintmsg ? errhint("%s", _(hintmsg)) : 0));
|
||||
|
||||
if (hintmsg)
|
||||
pfree(hintmsg);
|
||||
return 0;
|
||||
}
|
||||
if (!call_enum_check_hook(conf, &newval, &newextra,
|
||||
source, elevel))
|
||||
if (!validate_conf_option(record, name, value, source,
|
||||
elevel, false, &newval,
|
||||
&newextra))
|
||||
return 0;
|
||||
}
|
||||
else if (source == PGC_S_DEFAULT)
|
||||
@ -6309,6 +6456,295 @@ flatten_set_variable_args(const char *name, List *args)
|
||||
return buf.data;
|
||||
}
|
||||
|
||||
/*
|
||||
* Writes updated configuration parameter values into
|
||||
* postgresql.auto.conf.temp file. It traverses the list of parameters
|
||||
* and quote the string values before writing them to temporaray file.
|
||||
*/
|
||||
static void
|
||||
write_auto_conf_file(int fd, const char *filename, ConfigVariable **head_p)
|
||||
{
|
||||
ConfigVariable *item;
|
||||
StringInfoData buf;
|
||||
|
||||
initStringInfo(&buf);
|
||||
appendStringInfoString(&buf, "# Do not edit this file manually! \n");
|
||||
appendStringInfoString(&buf, "# It will be overwritten by ALTER SYSTEM command. \n");
|
||||
|
||||
/*
|
||||
* write the file header message before contents, so that if there is no
|
||||
* item it can contain message
|
||||
*/
|
||||
if (write(fd, buf.data, buf.len) < 0)
|
||||
ereport(ERROR,
|
||||
(errmsg("failed to write to \"%s\" file", filename)));
|
||||
resetStringInfo(&buf);
|
||||
|
||||
/*
|
||||
* traverse the list of parameters, quote the string parameter and write
|
||||
* it to file. Once all parameters are written fsync the file.
|
||||
*/
|
||||
|
||||
for (item = *head_p; item != NULL; item = item->next)
|
||||
{
|
||||
char *escaped;
|
||||
|
||||
appendStringInfoString(&buf, item->name);
|
||||
appendStringInfoString(&buf, " = ");
|
||||
|
||||
appendStringInfoString(&buf, "\'");
|
||||
escaped = escape_single_quotes_ascii(item->value);
|
||||
appendStringInfoString(&buf, escaped);
|
||||
free(escaped);
|
||||
appendStringInfoString(&buf, "\'");
|
||||
|
||||
appendStringInfoString(&buf, "\n");
|
||||
|
||||
if (write(fd, buf.data, buf.len) < 0)
|
||||
ereport(ERROR,
|
||||
(errmsg("failed to write to \"%s\" file", filename)));
|
||||
resetStringInfo(&buf);
|
||||
}
|
||||
|
||||
if (pg_fsync(fd) != 0)
|
||||
ereport(ERROR,
|
||||
(errcode_for_file_access(),
|
||||
errmsg("could not fsync file \"%s\": %m", filename)));
|
||||
|
||||
pfree(buf.data);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* This function takes list of all configuration parameters in
|
||||
* postgresql.auto.conf and parameter to be updated as input arguments and
|
||||
* replace the updated configuration parameter value in a list. If the
|
||||
* parameter to be updated is new then it is appended to the list of
|
||||
* parameters.
|
||||
*/
|
||||
static void
|
||||
replace_auto_config_value(ConfigVariable **head_p, ConfigVariable **tail_p,
|
||||
char *config_file,
|
||||
char *name, char *value)
|
||||
{
|
||||
ConfigVariable *item,
|
||||
*prev = NULL;
|
||||
|
||||
if (*head_p != NULL)
|
||||
{
|
||||
for (item = *head_p; item != NULL; item = item->next)
|
||||
{
|
||||
if (strcmp(item->name, name) == 0)
|
||||
{
|
||||
pfree(item->value);
|
||||
if (value != NULL)
|
||||
/* update the parameter value */
|
||||
item->value = pstrdup(value);
|
||||
else
|
||||
{
|
||||
/* delete the configuration parameter from list */
|
||||
if (*head_p == item)
|
||||
*head_p = item->next;
|
||||
else
|
||||
prev->next = item->next;
|
||||
|
||||
if (*tail_p == item)
|
||||
*tail_p = prev;
|
||||
|
||||
pfree(item->name);
|
||||
pfree(item->filename);
|
||||
pfree(item);
|
||||
}
|
||||
return;
|
||||
}
|
||||
prev = item;
|
||||
}
|
||||
}
|
||||
|
||||
if (value == NULL)
|
||||
return;
|
||||
|
||||
item = palloc(sizeof *item);
|
||||
item->name = pstrdup(name);
|
||||
item->value = pstrdup(value);
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Persist the configuration parameter value.
|
||||
*
|
||||
* This function takes all previous configuration parameters
|
||||
* set by ALTER SYSTEM command and the currently set ones
|
||||
* and write them all to the automatic configuration file.
|
||||
*
|
||||
* The configuration parameters are written to a temporary
|
||||
* file then renamed to the final name. The template for the
|
||||
* temporary file is postgresql.auto.conf.temp.
|
||||
*
|
||||
* An LWLock is used to serialize writing to the same file.
|
||||
*
|
||||
* In case of an error, we leave the original automatic
|
||||
* configuration file (postgresql.auto.conf) intact.
|
||||
*/
|
||||
void
|
||||
AlterSystemSetConfigFile(AlterSystemStmt * altersysstmt)
|
||||
{
|
||||
char *name;
|
||||
char *value;
|
||||
int Tmpfd = -1;
|
||||
FILE *infile;
|
||||
struct config_generic *record;
|
||||
ConfigVariable *head = NULL;
|
||||
ConfigVariable *tail = NULL;
|
||||
char AutoConfFileName[MAXPGPATH];
|
||||
char AutoConfTmpFileName[MAXPGPATH];
|
||||
struct stat st;
|
||||
void *newextra = NULL;
|
||||
|
||||
if (!superuser())
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
||||
(errmsg("must be superuser to execute ALTER SYSTEM command"))));
|
||||
|
||||
/*
|
||||
* Validate the name and arguments [value1, value2 ... ].
|
||||
*/
|
||||
name = altersysstmt->setstmt->name;
|
||||
|
||||
switch (altersysstmt->setstmt->kind)
|
||||
{
|
||||
case VAR_SET_VALUE:
|
||||
value = ExtractSetVariableArgs(altersysstmt->setstmt);
|
||||
break;
|
||||
|
||||
case VAR_SET_DEFAULT:
|
||||
value = NULL;
|
||||
break;
|
||||
default:
|
||||
elog(ERROR, "unrecognized alter system stmt type: %d",
|
||||
altersysstmt->setstmt->kind);
|
||||
break;
|
||||
}
|
||||
|
||||
record = find_option(name, false, LOG);
|
||||
if (record == NULL)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
||||
errmsg("unrecognized configuration parameter \"%s\"", name)));
|
||||
|
||||
if ((record->context == PGC_INTERNAL) ||
|
||||
(record->flags & GUC_DISALLOW_IN_FILE))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_CANT_CHANGE_RUNTIME_PARAM),
|
||||
errmsg("parameter \"%s\" cannot be changed",
|
||||
name)));
|
||||
|
||||
if (!validate_conf_option(record, name, value, PGC_S_FILE,
|
||||
ERROR, true, NULL,
|
||||
&newextra))
|
||||
ereport(ERROR,
|
||||
(errmsg("invalid value for parameter \"%s\": \"%s\"", name, value)));
|
||||
|
||||
|
||||
/*
|
||||
* Use data directory as reference path for postgresql.auto.conf and it's
|
||||
* corresponding temp file
|
||||
*/
|
||||
join_path_components(AutoConfFileName, data_directory, PG_AUTOCONF_FILENAME);
|
||||
canonicalize_path(AutoConfFileName);
|
||||
snprintf(AutoConfTmpFileName, sizeof(AutoConfTmpFileName), "%s.%s",
|
||||
AutoConfFileName,
|
||||
"temp");
|
||||
|
||||
/*
|
||||
* one backend is allowed to operate on postgresql.auto.conf file, to
|
||||
* ensure that we need to update the contents of the file with
|
||||
* AutoFileLock. To ensure crash safety, first the contents are written to
|
||||
* temporary file and then rename it to postgresql.auto.conf. In case
|
||||
* there exists a temp file from previous crash, that can be reused.
|
||||
*/
|
||||
|
||||
LWLockAcquire(AutoFileLock, LW_EXCLUSIVE);
|
||||
|
||||
Tmpfd = open(AutoConfTmpFileName, O_CREAT | O_RDWR | O_TRUNC, S_IRUSR | S_IWUSR);
|
||||
if (Tmpfd < 0)
|
||||
ereport(ERROR,
|
||||
(errcode_for_file_access(),
|
||||
errmsg("failed to open auto conf temp file \"%s\": %m ",
|
||||
AutoConfTmpFileName)));
|
||||
|
||||
PG_TRY();
|
||||
{
|
||||
if (stat(AutoConfFileName, &st) == 0)
|
||||
{
|
||||
/* open postgresql.auto.conf file */
|
||||
infile = AllocateFile(AutoConfFileName, "r");
|
||||
if (infile == NULL)
|
||||
ereport(ERROR,
|
||||
(errmsg("failed to open auto conf file \"%s\": %m ",
|
||||
AutoConfFileName)));
|
||||
|
||||
/* Parse the postgresql.auto.conf file */
|
||||
ParseConfigFp(infile, AutoConfFileName, 0, LOG, &head, &tail);
|
||||
|
||||
FreeFile(infile);
|
||||
}
|
||||
|
||||
/*
|
||||
* replace with new value if the configuration parameter already
|
||||
* exists OR add it as a new cofiguration parameter in the file.
|
||||
*/
|
||||
replace_auto_config_value(&head, &tail, AutoConfFileName, name, value);
|
||||
|
||||
/* Write and sync the New contents to postgresql.auto.conf.temp file */
|
||||
write_auto_conf_file(Tmpfd, AutoConfTmpFileName, &head);
|
||||
|
||||
close(Tmpfd);
|
||||
Tmpfd = -1;
|
||||
|
||||
/*
|
||||
* As the rename is atomic operation, if any problem occurs after this
|
||||
* at max it can loose the parameters set by last ALTER SYSTEM
|
||||
* command.
|
||||
*/
|
||||
if (rename(AutoConfTmpFileName, AutoConfFileName) < 0)
|
||||
ereport(ERROR,
|
||||
(errcode_for_file_access(),
|
||||
errmsg("could not rename file \"%s\" to \"%s\" : %m",
|
||||
AutoConfTmpFileName, AutoConfFileName)));
|
||||
}
|
||||
PG_CATCH();
|
||||
{
|
||||
if (Tmpfd >= 0)
|
||||
close(Tmpfd);
|
||||
|
||||
unlink(AutoConfTmpFileName);
|
||||
FreeConfigVariables(head);
|
||||
PG_RE_THROW();
|
||||
}
|
||||
PG_END_TRY();
|
||||
|
||||
FreeConfigVariables(head);
|
||||
LWLockRelease(AutoFileLock);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* SET command
|
||||
|
Reference in New Issue
Block a user