*** a/doc/src/sgml/config.sgml --- b/doc/src/sgml/config.sgml *************** *** 158,163 **** SET ENABLE_SEQSCAN TO OFF; --- 158,176 ---- require superuser permission to change via SET or ALTER. + + + Another way to change configuration parameters persistently is by + use of + command, for example: + + ALTER SYSTEM SET checkpoint_timeout TO 600; + + This command will allow users to change values persistently + through SQL command. The values will be effective after reload of server configuration + (SIGHUP) or server startup. The effect of this command is similar to when + user manually changes values in postgresql.conf. + *** a/doc/src/sgml/ref/allfiles.sgml --- b/doc/src/sgml/ref/allfiles.sgml *************** *** 30,35 **** Complete list of usable sgml source files in this directory. --- 30,36 ---- + *** /dev/null --- b/doc/src/sgml/ref/alter_system.sgml *************** *** 0 **** --- 1,114 ---- + + + + + ALTER SYSTEM + 7 + SQL - Language Statements + + + + ALTER SYSTEM + change a server configuration parameter + + + + ALTER SYSTEM + + + + + ALTER SYSTEM SET configuration_parameter { TO | = } { value | 'value' | DEFAULT } + + + + + Description + + + ALTER SYSTEM writes the configuration parameter + values to the postgresql.auto.conf file. With + DEFAULT, it removes a configuration entry from + postgresql.auto.conf file. The values will be + effective after reload of server configuration (SIGHUP) or in next + server start based on the type of configuration parameter modified. + + + + This command is not allowed inside transaction block or function. + + + + See for other ways to set the parameters and + how they become effective. + + + + + Parameters + + + + configuration_parameter + + + Name of a settable run-time parameter. Available parameters are + documented in . + + + + + + value + + + New value of parameter. Values can be specified as string + constants, identifiers, numbers, or comma-separated lists of + these, as appropriate for the particular parameter. + DEFAULT can be written to specify to remove the + parameter and its value from postgresql.auto.conf + + + + + + + + Examples + + + Set the wal_level: + + ALTER SYSTEM SET wal_level = hot_standby; + + + + + Set the authentication_timeout: + + ALTER SYSTEM SET authentication_timeout = 10; + + + + + Compatibility + + + The ALTER SYSTEM statement is a + PostgreSQL extension. + + + + + See Also + + + + + + + + *** a/doc/src/sgml/reference.sgml --- b/doc/src/sgml/reference.sgml *************** *** 58,63 **** --- 58,64 ---- &alterSchema; &alterSequence; &alterServer; + &alterSystem; &alterTable; &alterTableSpace; &alterTSConfig; *** a/doc/src/sgml/storage.sgml --- b/doc/src/sgml/storage.sgml *************** *** 34,40 **** these required items, the cluster configuration files postgresql.conf, pg_hba.conf, and pg_ident.conf are traditionally stored in PGDATA (although in PostgreSQL 8.0 and ! later, it is possible to place them elsewhere). --- 34,41 ---- postgresql.conf, pg_hba.conf, and pg_ident.conf are traditionally stored in PGDATA (although in PostgreSQL 8.0 and ! later, it is possible to place them elsewhere). By default directory config is stored ! in PGDATA, however it needs to be kept along with postgresql.conf.
*************** *** 57,62 **** Item --- 58,68 ---- + config + Subdirectory containing generated configuration files + + + base Subdirectory containing per-database subdirectories *************** *** 253,258 **** where PPP is the PID of the owning backend and --- 259,269 ---- NNN distinguishes different temporary files of that backend. + + Configuration variables changed by command ALTER SYSTEM will be stored in + PGDATA/config. + + *** a/src/backend/nodes/copyfuncs.c --- b/src/backend/nodes/copyfuncs.c *************** *** 3247,3252 **** _copyRefreshMatViewStmt(const RefreshMatViewStmt *from) --- 3247,3262 ---- return newnode; } + static AlterSystemStmt * + _copyAlterSystemStmt(const AlterSystemStmt * from) + { + AlterSystemStmt *newnode = makeNode(AlterSystemStmt); + + COPY_NODE_FIELD(setstmt); + + return newnode; + } + static CreateSeqStmt * _copyCreateSeqStmt(const CreateSeqStmt *from) { *************** *** 4320,4325 **** copyObject(const void *from) --- 4330,4338 ---- case T_RefreshMatViewStmt: retval = _copyRefreshMatViewStmt(from); break; + case T_AlterSystemStmt: + retval = _copyAlterSystemStmt(from); + break; case T_CreateSeqStmt: retval = _copyCreateSeqStmt(from); break; *** a/src/backend/nodes/equalfuncs.c --- b/src/backend/nodes/equalfuncs.c *************** *** 1528,1533 **** _equalRefreshMatViewStmt(const RefreshMatViewStmt *a, const RefreshMatViewStmt * --- 1528,1542 ---- } static bool + _equalAlterSystemStmt(const AlterSystemStmt * a, const AlterSystemStmt * b) + { + COMPARE_NODE_FIELD(setstmt); + + return true; + } + + + static bool _equalCreateSeqStmt(const CreateSeqStmt *a, const CreateSeqStmt *b) { COMPARE_NODE_FIELD(sequence); *************** *** 2790,2795 **** equal(const void *a, const void *b) --- 2799,2807 ---- case T_RefreshMatViewStmt: retval = _equalRefreshMatViewStmt(a, b); break; + case T_AlterSystemStmt: + retval = _equalAlterSystemStmt(a, b); + break; case T_CreateSeqStmt: retval = _equalCreateSeqStmt(a, b); break; *** a/src/backend/parser/gram.y --- b/src/backend/parser/gram.y *************** *** 216,222 **** static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); AlterEventTrigStmt AlterDatabaseStmt AlterDatabaseSetStmt AlterDomainStmt AlterEnumStmt AlterFdwStmt AlterForeignServerStmt AlterGroupStmt ! AlterObjectSchemaStmt AlterOwnerStmt AlterSeqStmt AlterTableStmt AlterExtensionStmt AlterExtensionContentsStmt AlterForeignTableStmt AlterCompositeTypeStmt AlterUserStmt AlterUserMappingStmt AlterUserSetStmt AlterRoleStmt AlterRoleSetStmt --- 216,222 ---- AlterEventTrigStmt AlterDatabaseStmt AlterDatabaseSetStmt AlterDomainStmt AlterEnumStmt AlterFdwStmt AlterForeignServerStmt AlterGroupStmt ! AlterObjectSchemaStmt AlterOwnerStmt AlterSeqStmt AlterSystemStmt AlterTableStmt AlterExtensionStmt AlterExtensionContentsStmt AlterForeignTableStmt AlterCompositeTypeStmt AlterUserStmt AlterUserMappingStmt AlterUserSetStmt AlterRoleStmt AlterRoleSetStmt *************** *** 395,401 **** static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); %type insert_rest ! %type set_rest set_rest_more SetResetClause FunctionSetResetClause %type TableElement TypedTableElement ConstraintElem TableFuncElement %type columnDef columnOptions --- 395,401 ---- %type insert_rest ! %type generic_set set_rest set_rest_more SetResetClause FunctionSetResetClause %type TableElement TypedTableElement ConstraintElem TableFuncElement %type columnDef columnOptions *************** *** 719,724 **** stmt : --- 719,725 ---- | AlterObjectSchemaStmt | AlterOwnerStmt | AlterSeqStmt + | AlterSystemStmt | AlterTableStmt | AlterCompositeTypeStmt | AlterRoleSetStmt *************** *** 1328,1334 **** set_rest: | set_rest_more ; ! set_rest_more: /* Generic SET syntaxes: */ var_name TO var_list { VariableSetStmt *n = makeNode(VariableSetStmt); --- 1329,1335 ---- | set_rest_more ; ! generic_set: var_name TO var_list { VariableSetStmt *n = makeNode(VariableSetStmt); *************** *** 1359,1364 **** set_rest_more: /* Generic SET syntaxes: */ --- 1360,1368 ---- n->name = $1; $$ = n; } + + set_rest_more: /* Generic SET syntaxes: */ + generic_set {$$ = $1;} | var_name FROM CURRENT_P { VariableSetStmt *n = makeNode(VariableSetStmt); *************** *** 8242,8247 **** DropdbStmt: DROP DATABASE database_name --- 8246,8267 ---- /***************************************************************************** * + * ALTER SYSTEM SET + * + * Command to edit postgresql.conf + *****************************************************************************/ + + AlterSystemStmt: + ALTER SYSTEM_P SET generic_set + { + AlterSystemStmt *n = makeNode(AlterSystemStmt); + n->setstmt = $4; + $$ = (Node *)n; + } + + + /***************************************************************************** + * * Manipulate a domain * *****************************************************************************/ *** a/src/backend/replication/basebackup.c --- b/src/backend/replication/basebackup.c *************** *** 803,808 **** sendDir(char *path, int basepathlen, bool sizeonly) --- 803,815 ---- strlen(PG_TEMP_FILE_PREFIX)) == 0) continue; + /* skip auto conf temporary file */ + if (strncmp(de->d_name, + PG_AUTOCONF_FILENAME, + sizeof(PG_AUTOCONF_FILENAME)) == 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 *************** *** 687,692 **** standard_ProcessUtility(Node *parsetree, --- 687,697 ---- ExplainQuery((ExplainStmt *) parsetree, queryString, params, dest); break; + case T_AlterSystemStmt: + PreventTransactionChain(isTopLevel, "ALTER SYSTEM"); + AlterSystemSetConfigFile((AlterSystemStmt *) parsetree); + break; + case T_VariableSetStmt: ExecSetVariableStmt((VariableSetStmt *) parsetree); break; *************** *** 2155,2160 **** CreateCommandTag(Node *parsetree) --- 2160,2169 ---- tag = "REFRESH MATERIALIZED VIEW"; break; + case T_AlterSystemStmt: + tag = "ALTER SYSTEM"; + break; + case T_VariableSetStmt: switch (((VariableSetStmt *) parsetree)->kind) { *************** *** 2721,2726 **** GetCommandLogLevel(Node *parsetree) --- 2730,2739 ---- lev = LOGSTMT_DDL; break; + case T_AlterSystemStmt: + lev = LOGSTMT_ALL; + break; + case T_VariableSetStmt: lev = LOGSTMT_ALL; break; *** a/src/backend/utils/misc/guc-file.l --- b/src/backend/utils/misc/guc-file.l *************** *** 134,139 **** ProcessConfigFile(GucContext context) --- 134,146 ---- */ elevel = IsUnderPostmaster ? DEBUG2 : LOG; + /* + * to warn user if postgresql.auto.conf is not accessible + * so that he doesn't expect parameters set by ALTER SYSTEM + * command will be effective + */ + autoconf_errcause = AUTOCONF_DIR_NOT_PARSED; + /* Parse the file into a list of option names and values */ head = tail = NULL; *************** *** 144,149 **** ProcessConfigFile(GucContext context) --- 151,170 ---- goto cleanup_list; } + if (autoconf_errcause != AUTOCONF_FILE_ACCESS && context != PGC_SIGHUP) + { + if (autoconf_errcause == AUTOCONF_DIR_PARSED) + ereport(elevel, + (errmsg("configuration parameters changed with ALTER SYSTEM command will not be effective"), + errdetail("file \"%s\" containing configuration parameters is not accessible.", PG_AUTOCONF_FILENAME))); + + else + ereport(elevel, + (errmsg("configuration parameters changed with ALTER SYSTEM command will not be effective"), + errdetail("default include directive for \"%s\" folder is not present in postgresql.conf", PG_AUTOCONF_DIR))); + + } + /* * Mark all extant GUC variables as not present in the config file. * We need this so that we can tell below which ones have been removed *************** *** 690,695 **** cleanup: --- 711,720 ---- /* * Read and parse all config files in a subdirectory in alphabetical order + * + * While parsing, it records if it has parsed postgresql.auto.conf file. + * This information can be used after parsing to ensure if the parameters + * set by ALTER SYSTEM SET command will be effective. */ bool ParseConfigDirectory(const char *includedir, *************** *** 705,710 **** ParseConfigDirectory(const char *includedir, --- 730,740 ---- int num_filenames = 0; int size_filenames = 0; bool status; + char *ConfigAutoDir; + char ConfigDir[MAXPGPATH]; + char *ConfigAutoFileName; + char Filename[MAXPGPATH]; + directory = AbsoluteConfigLocation(includedir, calling_file); d = AllocateDir(directory); *************** *** 717,722 **** ParseConfigDirectory(const char *includedir, --- 747,761 ---- return false; } + /* + * remember the accessability of config directory and auto conf file + * so that it can be used after parsing for giving warning to user + */ + snprintf(ConfigDir,sizeof(ConfigDir),"%s", PG_AUTOCONF_DIR); + ConfigAutoDir = AbsoluteConfigLocation(ConfigDir, ConfigFileName); + if (depth == 1 && strcmp(ConfigAutoDir, directory) == 0) + autoconf_errcause = AUTOCONF_DIR_PARSED; + /* * Read the directory and put the filenames in an array, so we can sort * them prior to processing the contents. *************** *** 744,749 **** ParseConfigDirectory(const char *includedir, --- 783,793 ---- { if (!S_ISDIR(st.st_mode)) { + snprintf(Filename,sizeof(Filename),"%s/%s", PG_AUTOCONF_DIR, PG_AUTOCONF_FILENAME); + ConfigAutoFileName = AbsoluteConfigLocation(Filename, ConfigFileName); + if (depth == 1 && strcmp(ConfigAutoFileName, filename) == 0) + autoconf_errcause = AUTOCONF_FILE_ACCESS; + /* Add file to list, increasing its size in blocks of 32 */ if (num_filenames == size_filenames) { *** a/src/backend/utils/misc/guc.c --- b/src/backend/utils/misc/guc.c *************** *** 57,68 **** --- 57,70 ---- #include "postmaster/postmaster.h" #include "postmaster/syslogger.h" #include "postmaster/walwriter.h" + #include "port.h" #include "replication/syncrep.h" #include "replication/walreceiver.h" #include "replication/walsender.h" #include "storage/bufmgr.h" #include "storage/standby.h" #include "storage/fd.h" + #include "storage/lwlock.h" #include "storage/proc.h" #include "storage/predicate.h" #include "tcop/tcopprot.h" *************** *** 3377,3382 **** static void ShowAllGUCConfig(DestReceiver *dest); --- 3379,3387 ---- 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); /* *************** *** 5139,5144 **** config_enum_get_options(struct config_enum * record, const char *prefix, --- 5144,5363 ---- 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 + */ + int + validate_conf_option(struct config_generic * record, + const char *name, const char *value, 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, + PGC_S_FILE, 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, + PGC_S_FILE, 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, + PGC_S_FILE, 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, + PGC_S_FILE, 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, + PGC_S_FILE, LOG)) + return 0; + + if (*newextra && freemem) + free(*newextra); + } + } + break; + } + return 1; + } + /* * Sets option `name' to given value. *************** *** 5387,5402 **** 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)) return 0; } else if (source == PGC_S_DEFAULT) --- 5606,5613 ---- if (value) { ! if (!validate_conf_option(record, name, value, elevel, ! false, &newval, &newextra)) return 0; } else if (source == PGC_S_DEFAULT) *************** *** 5480,5507 **** 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)) return 0; } else if (source == PGC_S_DEFAULT) { --- 5691,5700 ---- if (value) { ! if (!validate_conf_option(record, name, value, elevel, ! false, &newval, &newextra)) return 0; + } else if (source == PGC_S_DEFAULT) { *************** *** 5584,5608 **** 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)) return 0; } else if (source == PGC_S_DEFAULT) { --- 5777,5786 ---- if (value) { ! if (!validate_conf_option(record, name, value, elevel, ! false, &newval, &newextra)) return 0; + } else if (source == PGC_S_DEFAULT) { *************** *** 5685,5711 **** 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) 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) { --- 5863,5872 ---- if (value) { ! if (!validate_conf_option(record, name, value, elevel, ! false, &newval, &newextra)) return 0; } else if (source == PGC_S_DEFAULT) { *************** *** 5811,5837 **** 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)) return 0; } else if (source == PGC_S_DEFAULT) { --- 5972,5981 ---- if (value) { ! if (!validate_conf_option(record, name, value, elevel, ! false, &newval, &newextra)) return 0; + } else if (source == PGC_S_DEFAULT) { *************** *** 6200,6205 **** flatten_set_variable_args(const char *name, List *args) --- 6344,6664 ---- 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"); + + /* + * traverse the list of parameters, quote the string parameter and write + * it 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. + * A stale temporary file may be left behind in case we crash. + * Such files are removed on the next server restart. + */ + void + AlterSystemSetConfigFile(AlterSystemStmt * altersysstmt) + { + char *name; + char *value; + int Tmpfd = -1; + FILE *infile; + struct config_generic *record; + ConfigVariable *head = NULL; + ConfigVariable *tail = NULL; + char ConfigFileDir[MAXPGPATH]; + 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, true, 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, ERROR, + true, NULL, &newextra)) + ereport(ERROR, + (errmsg("invalid value for parameter \"%s\": \"%s\"", name, value))); + + + /* + * The "config" directory should follow the postgresql.conf file directory + * not the data_directory. The same is validated while parsing the + * postgresql.conf configuration file. So while framing the name for + * postgresql.auto.conf and it's temp file we need to follow the + * postgresql.conf file directory. + */ + StrNCpy(ConfigFileDir, ConfigFileName, sizeof(ConfigFileDir)); + get_parent_directory(ConfigFileDir); + + /* Frame config dir, auto conf file and auto conf sample temp file names */ + snprintf(AutoConfFileName, sizeof(AutoConfFileName), "%s/%s/%s", + ConfigFileDir, + PG_AUTOCONF_DIR, + PG_AUTOCONF_FILENAME); + snprintf(AutoConfTmpFileName, sizeof(AutoConfTmpFileName), "%s/%s/%s.%s", + ConfigFileDir, + PG_AUTOCONF_DIR, + PG_AUTOCONF_FILENAME, + "temp"); + + /* + * Verify if include_dir for postgresql.auto.conf file exists in + * postgresql.conf. If it doesn't exist then warn the user that parameters + * changed with this command will not be effective. + */ + autoconf_errcause = AUTOCONF_DIR_NOT_PARSED; + + if (!ParseConfigFile(ConfigFileName, NULL, false, 0, LOG, &head, &tail)) + ereport(ERROR, + (errmsg("configuration file \"%s\" contains errors; parameters changed by " + "this command will not be effective", ConfigFileName))); + + /* + * ignore the list of options as we are intersted to just ensure the + * existence of include directive for config folder. + */ + head = tail = NULL; + + if (autoconf_errcause == AUTOCONF_DIR_NOT_PARSED) + ereport(WARNING, + (errmsg("configuration parameters changed by this command will not be effective"), + errdetail("default include directive for \"%s\" folder is not present in postgresql.conf", PG_AUTOCONF_DIR))); + + /* + * one backend is allowed to operate on .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 + */ + + 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 *** a/src/backend/utils/misc/postgresql.conf.sample --- b/src/backend/utils/misc/postgresql.conf.sample *************** *** 583,588 **** --- 583,597 ---- #include_if_exists = 'exists.conf' # include file only if it exists #include = 'special.conf' # include file + # This includes the default configuration directory included to support + # ALTER SYSTEM statement. + # + # WARNING: User should not remove below include_dir or directory config, + # as both are required to make ALTER SYSTEM command work. + # Any configuration parameter values specified after this line + # will override the values set by ALTER SYSTEM statement. + #include_dir = 'config' + #------------------------------------------------------------------------------ # CUSTOMIZED OPTIONS *** a/src/bin/initdb/initdb.c --- b/src/bin/initdb/initdb.c *************** *** 193,199 **** const char *subdirs[] = { "base/1", "pg_tblspc", "pg_stat", ! "pg_stat_tmp" }; --- 193,200 ---- "base/1", "pg_tblspc", "pg_stat", ! "pg_stat_tmp", ! PG_AUTOCONF_DIR }; *************** *** 1182,1187 **** setup_config(void) --- 1183,1189 ---- char repltok[MAXPGPATH]; char path[MAXPGPATH]; const char *default_timezone; + FILE *autofile; fputs(_("creating configuration files ... "), stdout); fflush(stdout); *************** *** 1264,1274 **** setup_config(void) --- 1266,1311 ---- conflines = replace_token(conflines, "#log_timezone = 'GMT'", repltok); } + snprintf(repltok, sizeof(repltok), "include_dir = '" PG_AUTOCONF_DIR "'"); + conflines = replace_token(conflines, "#include_dir = 'config'", repltok); + snprintf(path, sizeof(path), "%s/postgresql.conf", pg_data); writefile(path, conflines); chmod(path, S_IRUSR | S_IWUSR); + /* + * create the automatic configuration file to store the configuration + * parameters set by ALTER SYSTEM command. This file will be included in + * postgresql.conf and the parameters present in this file will override + * the values of parameters that exists before include of this file. + */ + sprintf(path, "%s/%s/%s", pg_data, PG_AUTOCONF_DIR, PG_AUTOCONF_FILENAME); + 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 (fputs("# Do not edit this file manually! It is overwritten by the ALTER SYSTEM command \n", + autofile) < 0) + { + fprintf(stderr, _("%s: could not write file \"%s\": %s\n"), + progname, path, strerror(errno)); + exit_nicely(); + } + + if (fclose(autofile)) + { + fprintf(stderr, _("%s: could not close file \"%s\": %s\n"), + progname, path, strerror(errno)); + exit_nicely(); + } + + chmod(path, S_IRUSR | S_IWUSR); + free(conflines); *** a/src/include/nodes/nodes.h --- b/src/include/nodes/nodes.h *************** *** 362,367 **** typedef enum NodeTag --- 362,368 ---- T_CreateEventTrigStmt, T_AlterEventTrigStmt, T_RefreshMatViewStmt, + T_AlterSystemStmt, /* * TAGS FOR PARSE TREE NODES (parsenodes.h) *** a/src/include/nodes/parsenodes.h --- b/src/include/nodes/parsenodes.h *************** *** 2393,2398 **** typedef struct DropdbStmt --- 2393,2408 ---- } DropdbStmt; /* ---------------------- + * Alter System Statement + * ---------------------- + */ + typedef struct AlterSystemStmt + { + NodeTag type; + VariableSetStmt *setstmt; /* SET subcommand */ + } AlterSystemStmt; + + /* ---------------------- * Cluster Statement (support pbrown's cluster index implementation) * ---------------------- */ *** a/src/include/pg_config_manual.h --- b/src/include/pg_config_manual.h *************** *** 281,283 **** --- 281,290 ---- /* #define HEAPDEBUGALL */ /* #define ACLDEBUG */ /* #define RTDEBUG */ + + /* + * Automatic configuration directory and file name + * for ALTER SYSTEM. + */ + #define PG_AUTOCONF_DIR "config" + #define PG_AUTOCONF_FILENAME "postgresql.auto.conf" *** a/src/include/storage/lwlock.h --- b/src/include/storage/lwlock.h *************** *** 79,84 **** typedef enum LWLockId --- 79,85 ---- SerializablePredicateLockListLock, OldSerXidLock, SyncRepLock, + AutoFileLock, /* Individual lock IDs end here */ FirstBufMappingLock, FirstLockMgrLock = FirstBufMappingLock + NUM_BUFFER_PARTITIONS, *** a/src/include/utils/guc.h --- b/src/include/utils/guc.h *************** *** 167,172 **** typedef enum --- 167,192 ---- #define GUC_QUALIFIER_SEPARATOR '.' /* + * Saved info for Auto Config Directory parse error + * We have to detect 2 different errors with these 3 enum values + * Error's to be detected are + * a. include directive for auto conf file is commented/missed from postgresql.conf file + * b. file postgresql.auto.conf is not accessible + * Before parsing config file initialize autoconf_errcause to AUTOCONF_DIR_NOT_PARSED + * During parse, it will set autoconf_errcause to AUTOCONF_DIR_PARSED, if config directory is parsed + * and set autoconf_errcause to AUTOCONF_FILE_ACCESS if file postgresql.conf is accessible + * After parse based on value of autoconf_errcause, it will distinguish between above 2 errors + */ + typedef enum + { + AUTOCONF_DIR_NOT_PARSED, + AUTOCONF_DIR_PARSED, + AUTOCONF_FILE_ACCESS + } AutoConfErrorCause; + + AutoConfErrorCause autoconf_errcause; + + /* * bit values in "flags" of a GUC variable */ #define GUC_LIST_INPUT 0x0001 /* input can be list format */ *************** *** 322,327 **** extern bool parse_real(const char *value, double *result); --- 342,348 ---- extern int set_config_option(const char *name, const char *value, GucContext context, GucSource source, GucAction action, bool changeVal, int elevel); + extern void AlterSystemSetConfigFile(AlterSystemStmt * setstmt); extern char *GetConfigOptionByName(const char *name, const char **varname); extern void GetConfigOptionByNum(int varnum, const char **values, bool *noshow); extern int GetNumConfigOptions(void); *** /dev/null --- b/src/test/regress/expected/alter_system.out *************** *** 0 **** --- 1,553 ---- + -- parameter can only be set at during server start. + alter system set max_connections To 10; + alter system set wal_level = hot_standby; + -- parameter can set by the backend + alter system set authentication_timeout = 10; + alter system set vacuum_cost_delay To 200; + ERROR: 200 is outside the valid range for parameter "vacuum_cost_delay" (0 .. 100) + alter system set bgwriter_lru_multiplier = 3.2; + alter system set full_page_writes = off; + alter system set synchronous_commit To off; + alter system set synchronous_standby_names = 'standby1, standby2'; + alter system set seq_page_cost = 0.9; + alter system set autovacuum_naptime To 30; + alter system set search_path = 'public'; + alter system set default_tablespace = ''; + alter system set checkpoint_timeout = 1; + ERROR: 1 is outside the valid range for parameter "checkpoint_timeout" (30 .. 3600) + alter system set checkpoint_timeout = 3700; + ERROR: 3700 is outside the valid range for parameter "checkpoint_timeout" (30 .. 3600) + alter system set checkpoint_timeout = 90; + alter system set restart_after_crash TO on; + alter system set work_mem = '2 MB'; + show max_connections; + max_connections + ----------------- + 100 + (1 row) + + show wal_level; + wal_level + ----------- + minimal + (1 row) + + show authentication_timeout; + authentication_timeout + ------------------------ + 1min + (1 row) + + show vacuum_cost_delay; + vacuum_cost_delay + ------------------- + 0 + (1 row) + + show bgwriter_lru_multiplier; + bgwriter_lru_multiplier + ------------------------- + 2 + (1 row) + + show full_page_writes; + full_page_writes + ------------------ + on + (1 row) + + show synchronous_commit; + synchronous_commit + -------------------- + on + (1 row) + + show synchronous_standby_names; + synchronous_standby_names + --------------------------- + + (1 row) + + show seq_page_cost; + seq_page_cost + --------------- + 1 + (1 row) + + show autovacuum_naptime; + autovacuum_naptime + -------------------- + 1min + (1 row) + + show search_path; + search_path + ---------------- + "$user",public + (1 row) + + show default_tablespace; + default_tablespace + -------------------- + + (1 row) + + show checkpoint_timeout; + checkpoint_timeout + -------------------- + 5min + (1 row) + + show restart_after_crash; + restart_after_crash + --------------------- + on + (1 row) + + show work_mem; + work_mem + ---------- + 1MB + (1 row) + + -- reload the new configurations. + select pg_reload_conf(); + pg_reload_conf + ---------------- + t + (1 row) + + select pg_sleep(1); + pg_sleep + ---------- + + (1 row) + + show max_connections; + max_connections + ----------------- + 100 + (1 row) + + show wal_level; + wal_level + ----------- + minimal + (1 row) + + show authentication_timeout; + authentication_timeout + ------------------------ + 10s + (1 row) + + show vacuum_cost_delay; + vacuum_cost_delay + ------------------- + 0 + (1 row) + + show bgwriter_lru_multiplier; + bgwriter_lru_multiplier + ------------------------- + 3.2 + (1 row) + + show full_page_writes; + full_page_writes + ------------------ + off + (1 row) + + show synchronous_commit; + synchronous_commit + -------------------- + off + (1 row) + + show synchronous_standby_names; + synchronous_standby_names + --------------------------- + standby1, standby2 + (1 row) + + show seq_page_cost; + seq_page_cost + --------------- + 0.9 + (1 row) + + show autovacuum_naptime; + autovacuum_naptime + -------------------- + 30s + (1 row) + + show search_path; + search_path + ------------- + public + (1 row) + + show default_tablespace; + default_tablespace + -------------------- + + (1 row) + + show checkpoint_timeout; + checkpoint_timeout + -------------------- + 90s + (1 row) + + show restart_after_crash; + restart_after_crash + --------------------- + on + (1 row) + + show work_mem; + work_mem + ---------- + 2MB + (1 row) + + -- reconnect to see the new configurations + \c + select name, setting from pg_settings where name = 'max_connections'; + name | setting + -----------------+--------- + max_connections | 100 + (1 row) + + select name, setting from pg_settings where name = 'wal_level'; + name | setting + -----------+--------- + wal_level | minimal + (1 row) + + select name, setting from pg_settings where name = 'authentication_timeout'; + name | setting + ------------------------+--------- + authentication_timeout | 10 + (1 row) + + select name, setting from pg_settings where name = 'vacuum_cost_delay'; + name | setting + -------------------+--------- + vacuum_cost_delay | 0 + (1 row) + + select name, setting from pg_settings where name = 'bgwriter_lru_multiplier'; + name | setting + -------------------------+--------- + bgwriter_lru_multiplier | 3.2 + (1 row) + + select name, setting from pg_settings where name = 'full_page_writes'; + name | setting + ------------------+--------- + full_page_writes | off + (1 row) + + select name, setting from pg_settings where name = 'synchronous_commit'; + name | setting + --------------------+--------- + synchronous_commit | off + (1 row) + + select name, setting from pg_settings where name = 'synchronous_standby_names'; + name | setting + ---------------------------+-------------------- + synchronous_standby_names | standby1, standby2 + (1 row) + + select name, setting from pg_settings where name = 'seq_page_cost'; + name | setting + ---------------+--------- + seq_page_cost | 0.9 + (1 row) + + select name, setting from pg_settings where name = 'autovacuum_naptime'; + name | setting + --------------------+--------- + autovacuum_naptime | 30 + (1 row) + + select name, setting from pg_settings where name = 'search_path'; + name | setting + -------------+--------- + search_path | public + (1 row) + + select name, setting from pg_settings where name = 'default_tablespace'; + name | setting + --------------------+--------- + default_tablespace | + (1 row) + + select name, setting from pg_settings where name = 'checkpoint_timeout'; + name | setting + --------------------+--------- + checkpoint_timeout | 90 + (1 row) + + select name, setting from pg_settings where name = 'restart_after_crash'; + name | setting + ---------------------+--------- + restart_after_crash | on + (1 row) + + select name, setting from pg_settings where name = 'work_mem'; + name | setting + ----------+--------- + work_mem | 2048 + (1 row) + + -- Reset the some of the parameters which are set in above + alter system set max_connections To default; + alter system set authentication_timeout To default; + alter system set synchronous_commit = default; + alter system set restart_after_crash To default; + -- reload the new configurations. + select pg_reload_conf(); + pg_reload_conf + ---------------- + t + (1 row) + + select pg_sleep(1); + pg_sleep + ---------- + + (1 row) + + select name, setting from pg_settings where name = 'max_connections'; + name | setting + -----------------+--------- + max_connections | 100 + (1 row) + + select name, setting from pg_settings where name = 'authentication_timeout'; + name | setting + ------------------------+--------- + authentication_timeout | 60 + (1 row) + + select name, setting from pg_settings where name = 'seq_page_cost'; + name | setting + ---------------+--------- + seq_page_cost | 0.9 + (1 row) + + select name, setting from pg_settings where name = 'restart_after_crash'; + name | setting + ---------------------+--------- + restart_after_crash | on + (1 row) + + -- reconnect to see the new configurations + \c + show max_connections; + max_connections + ----------------- + 100 + (1 row) + + show authentication_timeout; + authentication_timeout + ------------------------ + 1min + (1 row) + + show seq_page_cost; + seq_page_cost + --------------- + 0.9 + (1 row) + + show restart_after_crash; + restart_after_crash + --------------------- + on + (1 row) + + -- set the parameters again which are reset above. + alter system set max_connections = 50; + alter system set authentication_timeout = 20; + alter system set synchronous_commit To off; + alter system set restart_after_crash To yes; + -- reload the new configurations. + select pg_reload_conf(); + pg_reload_conf + ---------------- + t + (1 row) + + select pg_sleep(1); + pg_sleep + ---------- + + (1 row) + + show max_connections; + max_connections + ----------------- + 100 + (1 row) + + show authentication_timeout; + authentication_timeout + ------------------------ + 20s + (1 row) + + show seq_page_cost; + seq_page_cost + --------------- + 0.9 + (1 row) + + show restart_after_crash; + restart_after_crash + --------------------- + on + (1 row) + + -- reconnect to see the new configurations + \c + show max_connections; + max_connections + ----------------- + 100 + (1 row) + + show authentication_timeout; + authentication_timeout + ------------------------ + 20s + (1 row) + + show seq_page_cost; + seq_page_cost + --------------- + 0.9 + (1 row) + + show restart_after_crash; + restart_after_crash + --------------------- + on + (1 row) + + -- set a configuration param inside a function + create or replace function persistent_func() returns integer as + $$ + begin + alter system set enable_hashagg=off; + return 1; + end; + $$ Language plpgsql; + select persistent_func(); + ERROR: ALTER SYSTEM cannot be executed from a function or multi-command string + CONTEXT: SQL statement "alter system set enable_hashagg=off" + PL/pgSQL function persistent_func() line 3 at SQL statement + show enable_hashagg; + enable_hashagg + ---------------- + on + (1 row) + + select pg_reload_conf(); + pg_reload_conf + ---------------- + t + (1 row) + + select pg_sleep(1); + pg_sleep + ---------- + + (1 row) + + show enable_hashagg; + enable_hashagg + ---------------- + on + (1 row) + + create or replace function persistent_func() returns integer as + $$ + begin + alter system set enable_hashagg=default; + return 1; + end; + $$ Language plpgsql; + select persistent_func(); + ERROR: ALTER SYSTEM cannot be executed from a function or multi-command string + CONTEXT: SQL statement "alter system set enable_hashagg=default" + PL/pgSQL function persistent_func() line 3 at SQL statement + select pg_reload_conf(); + pg_reload_conf + ---------------- + t + (1 row) + + select pg_sleep(1); + pg_sleep + ---------- + + (1 row) + + show enable_hashagg; + enable_hashagg + ---------------- + on + (1 row) + + Drop function persistent_func(); + -- Reset all parameters which are set at above. + alter system set max_connections = default; + alter system set wal_level To default; + alter system set authentication_timeout To default; + alter system set vacuum_cost_delay = default; + alter system set bgwriter_lru_multiplier To default; + alter system set full_page_writes To default; + alter system set synchronous_commit = default; + alter system set synchronous_standby_names To default; + alter system set seq_page_cost To default; + alter system set autovacuum_naptime = default; + alter system set search_path To default; + alter system set default_tablespace To default; + alter system set checkpoint_timeout To default; + alter system set restart_after_crash = default; + alter system set work_mem = default; + select pg_reload_conf(); + pg_reload_conf + ---------------- + t + (1 row) + + select pg_sleep(1); + pg_sleep + ---------- + + (1 row) + + -- Normal user tries to set + create user test password 'test'; + SET SESSION AUTHORIZATION test; + alter system set enable_seqscan=off; + ERROR: must be superuser to execute ALTER SYSTEM command + \c - + drop user test; + -- In a transaction block tries to set + start transaction; + alter system set enable_hashagg=off; + ERROR: ALTER SYSTEM cannot run inside a transaction block + commit; + start transaction; + alter system set enable_hashagg=default; + ERROR: ALTER SYSTEM cannot run inside a transaction block + commit; *** a/src/test/regress/parallel_schedule --- b/src/test/regress/parallel_schedule *************** *** 109,111 **** test: plancache limit plpgsql copy2 temp domain rangefuncs prepare without_oid c --- 109,114 ---- # run stats by itself because its delay may be insufficient under heavy load test: stats + + # run alter system by itself because it modifies the configuration parameters + test: alter_system *** a/src/test/regress/serial_schedule --- b/src/test/regress/serial_schedule *************** *** 138,140 **** test: largeobject --- 138,141 ---- test: with test: xml test: stats + test: alter_system *** /dev/null --- b/src/test/regress/sql/alter_system.sql *************** *** 0 **** --- 1,200 ---- + -- parameter can only be set at during server start. + alter system set max_connections To 10; + alter system set wal_level = hot_standby; + + -- parameter can set by the backend + alter system set authentication_timeout = 10; + alter system set vacuum_cost_delay To 200; + alter system set bgwriter_lru_multiplier = 3.2; + alter system set full_page_writes = off; + alter system set synchronous_commit To off; + alter system set synchronous_standby_names = 'standby1, standby2'; + alter system set seq_page_cost = 0.9; + alter system set autovacuum_naptime To 30; + alter system set search_path = 'public'; + alter system set default_tablespace = ''; + alter system set checkpoint_timeout = 1; + alter system set checkpoint_timeout = 3700; + alter system set checkpoint_timeout = 90; + alter system set restart_after_crash TO on; + alter system set work_mem = '2 MB'; + + + show max_connections; + show wal_level; + + show authentication_timeout; + show vacuum_cost_delay; + show bgwriter_lru_multiplier; + show full_page_writes; + show synchronous_commit; + show synchronous_standby_names; + show seq_page_cost; + show autovacuum_naptime; + show search_path; + show default_tablespace; + show checkpoint_timeout; + show restart_after_crash; + show work_mem; + + -- reload the new configurations. + select pg_reload_conf(); + select pg_sleep(1); + + show max_connections; + show wal_level; + + show authentication_timeout; + show vacuum_cost_delay; + show bgwriter_lru_multiplier; + show full_page_writes; + show synchronous_commit; + show synchronous_standby_names; + show seq_page_cost; + show autovacuum_naptime; + show search_path; + show default_tablespace; + show checkpoint_timeout; + show restart_after_crash; + show work_mem; + + + -- reconnect to see the new configurations + \c + + select name, setting from pg_settings where name = 'max_connections'; + select name, setting from pg_settings where name = 'wal_level'; + + select name, setting from pg_settings where name = 'authentication_timeout'; + select name, setting from pg_settings where name = 'vacuum_cost_delay'; + select name, setting from pg_settings where name = 'bgwriter_lru_multiplier'; + select name, setting from pg_settings where name = 'full_page_writes'; + select name, setting from pg_settings where name = 'synchronous_commit'; + select name, setting from pg_settings where name = 'synchronous_standby_names'; + select name, setting from pg_settings where name = 'seq_page_cost'; + select name, setting from pg_settings where name = 'autovacuum_naptime'; + select name, setting from pg_settings where name = 'search_path'; + select name, setting from pg_settings where name = 'default_tablespace'; + select name, setting from pg_settings where name = 'checkpoint_timeout'; + select name, setting from pg_settings where name = 'restart_after_crash'; + select name, setting from pg_settings where name = 'work_mem'; + + -- Reset the some of the parameters which are set in above + alter system set max_connections To default; + + alter system set authentication_timeout To default; + alter system set synchronous_commit = default; + alter system set restart_after_crash To default; + + -- reload the new configurations. + select pg_reload_conf(); + select pg_sleep(1); + + select name, setting from pg_settings where name = 'max_connections'; + select name, setting from pg_settings where name = 'authentication_timeout'; + select name, setting from pg_settings where name = 'seq_page_cost'; + select name, setting from pg_settings where name = 'restart_after_crash'; + + -- reconnect to see the new configurations + \c + + show max_connections; + show authentication_timeout; + show seq_page_cost; + show restart_after_crash; + + -- set the parameters again which are reset above. + alter system set max_connections = 50; + + alter system set authentication_timeout = 20; + alter system set synchronous_commit To off; + alter system set restart_after_crash To yes; + + -- reload the new configurations. + select pg_reload_conf(); + select pg_sleep(1); + + show max_connections; + show authentication_timeout; + show seq_page_cost; + show restart_after_crash; + + -- reconnect to see the new configurations + \c + show max_connections; + show authentication_timeout; + show seq_page_cost; + show restart_after_crash; + + -- set a configuration param inside a function + create or replace function persistent_func() returns integer as + $$ + begin + alter system set enable_hashagg=off; + return 1; + end; + $$ Language plpgsql; + + select persistent_func(); + + show enable_hashagg; + + select pg_reload_conf(); + select pg_sleep(1); + + show enable_hashagg; + + create or replace function persistent_func() returns integer as + $$ + begin + alter system set enable_hashagg=default; + return 1; + end; + $$ Language plpgsql; + + select persistent_func(); + + select pg_reload_conf(); + select pg_sleep(1); + + show enable_hashagg; + + Drop function persistent_func(); + + -- Reset all parameters which are set at above. + alter system set max_connections = default; + alter system set wal_level To default; + + alter system set authentication_timeout To default; + alter system set vacuum_cost_delay = default; + alter system set bgwriter_lru_multiplier To default; + alter system set full_page_writes To default; + alter system set synchronous_commit = default; + alter system set synchronous_standby_names To default; + alter system set seq_page_cost To default; + alter system set autovacuum_naptime = default; + alter system set search_path To default; + alter system set default_tablespace To default; + alter system set checkpoint_timeout To default; + alter system set restart_after_crash = default; + alter system set work_mem = default; + + select pg_reload_conf(); + select pg_sleep(1); + + + -- Normal user tries to set + create user test password 'test'; + SET SESSION AUTHORIZATION test; + alter system set enable_seqscan=off; + \c - + drop user test; + + -- In a transaction block tries to set + start transaction; + alter system set enable_hashagg=off; + commit; + + start transaction; + alter system set enable_hashagg=default; + commit;