<row>
<entry><link linkend="view-pg-file-settings"><structname>pg_file_settings</structname></link></entry>
- <entry>file location of parameter settings</entry>
+ <entry>summary of configuration file contents</entry>
</row>
<row>
</indexterm>
<para>
- The view <structname>pg_file_settings</structname> provides the file
- name, line number and value of all parameters which are set through
- configuration files.
- In contrast to <structname>pg_settings</structname>, a row is provided for
- each occurrence of the parameter across all configuration files. This is helpful
- for discovering why one value may have been used in preference to another
- when the parameters were loaded.
+ The view <structname>pg_file_settings</structname> provides a summary of
+ the contents of the server's configuration file(s). A row appears in
+ this view for each <quote>name = value</> entry appearing in the files,
+ with annotations indicating whether the value could be applied
+ successfully. Additional row(s) may appear for problems not linked to
+ a <quote>name = value</> entry, such as syntax errors in the files.
+ </para>
+
+ <para>
+ This view is helpful for checking whether planned changes in the
+ configuration files will work, or for diagnosing a previous failure.
+ Note that this view reports on the <emphasis>current</> contents of the
+ files, not on what was last applied by the server. (The
+ <link linkend="view-pg-settings"><structname>pg_settings</structname></link>
+ view is usually sufficient to determine that.)
+ </para>
+
+ <para>
+ The <structname>pg_file_settings</structname> view can be read only by
+ superusers.
</para>
<table>
<row>
<entry><structfield>sourcefile</structfield></entry>
<entry><structfield>text</structfield></entry>
- <entry>Path to and name of the configration file</entry>
+ <entry>Full pathname of the configuration file</entry>
</row>
<row>
<entry><structfield>sourceline</structfield></entry>
<entry><structfield>integer</structfield></entry>
<entry>
- Line number within the configuration file where the value was set
+ Line number within the configuration file where the entry appears
</entry>
</row>
<row>
<entry><structfield>seqno</structfield></entry>
<entry><structfield>integer</structfield></entry>
- <entry>Order in which the setting was loaded</entry>
+ <entry>Order in which the entries are processed (1..<replaceable>n</>)</entry>
</row>
<row>
<entry><structfield>name</structfield></entry>
<entry><structfield>text</structfield></entry>
- <entry>Run-time configuration parameter name</entry>
+ <entry>Configuration parameter name</entry>
</row>
<row>
<entry><structfield>setting</structfield></entry>
<entry><structfield>text</structfield></entry>
- <entry>value of the parameter</entry>
+ <entry>Value to be assigned to the parameter</entry>
+ </row>
+ <row>
+ <entry><structfield>applied</structfield></entry>
+ <entry><structfield>boolean</structfield></entry>
+ <entry>True if the value can be applied successfully</entry>
+ </row>
+ <row>
+ <entry><structfield>error</structfield></entry>
+ <entry><structfield>text</structfield></entry>
+ <entry>If not null, an error message indicating why this entry could
+ not be applied</entry>
</row>
</tbody>
</tgroup>
- </table>
+ </table>
<para>
- See <xref linkend="config-setting"> for more information about the various
- ways to change these parameters.
+ If the configuration file contains syntax errors or invalid parameter
+ names, the server will not attempt to apply any settings from it, and
+ therefore all the <structfield>applied</> fields will read as false.
+ In such a case there will be one or more rows with
+ non-null <structfield>error</structfield> fields indicating the
+ problem(s). Otherwise, individual settings will be applied if possible.
+ If an individual setting cannot be applied (e.g., invalid value, or the
+ setting cannot be changed after server start) it will have an appropriate
+ message in the <structfield>error</structfield> field. Another way that
+ an entry might have <structfield>applied</> = false is that it is
+ overridden by a later entry for the same parameter name; this case is not
+ considered an error so nothing appears in
+ the <structfield>error</structfield> field.
</para>
<para>
- The <structname>pg_file_settings</structname> view cannot be modified
- directly as it represents information, as read in at server start or
- reload time, about all parameter settings across all configuration files.
+ See <xref linkend="config-setting"> for more information about the various
+ ways to change run-time parameters.
</para>
</sect1>
static void FreeConfigVariable(ConfigVariable *item);
+static void record_config_file_error(const char *errmsg,
+ const char *config_file,
+ int lineno,
+ ConfigVariable **head_p,
+ ConfigVariable **tail_p);
+
static int GUC_flex_fatal(const char *msg);
static char *GUC_scanstr(const char *s);
* parameter indicates in what context the file is being read --- either
* postmaster startup (including standalone-backend startup) or SIGHUP.
* All options mentioned in the configuration file are set to new values.
- * If an error occurs, no values will be changed.
+ * If a hard error occurs, no values will be changed. (There can also be
+ * errors that prevent just one value from being changed.)
*/
void
ProcessConfigFile(GucContext context)
{
- bool error = false;
- bool apply = false;
int elevel;
- const char *ConfFileWithError;
- ConfigVariable *item,
- *head,
- *tail;
- int i;
- int file_variables_count = 0;
+ MemoryContext config_cxt;
+ MemoryContext caller_cxt;
/*
* Config files are processed on startup (by the postmaster only)
*/
elevel = IsUnderPostmaster ? DEBUG2 : LOG;
+ /*
+ * This function is usually called within a process-lifespan memory
+ * context. To ensure that any memory leaked during GUC processing does
+ * not accumulate across repeated SIGHUP cycles, do the work in a private
+ * context that we can free at exit.
+ */
+ config_cxt = AllocSetContextCreate(CurrentMemoryContext,
+ "config file processing",
+ ALLOCSET_DEFAULT_MINSIZE,
+ ALLOCSET_DEFAULT_MINSIZE,
+ ALLOCSET_DEFAULT_MAXSIZE);
+ caller_cxt = MemoryContextSwitchTo(config_cxt);
+
+ /*
+ * Read and apply the config file. We don't need to examine the result.
+ */
+ (void) ProcessConfigFileInternal(context, true, elevel);
+
+ /* Clean up */
+ MemoryContextSwitchTo(caller_cxt);
+ MemoryContextDelete(config_cxt);
+}
+
+/*
+ * This function handles both actual config file (re)loads and execution of
+ * show_all_file_settings() (i.e., the pg_file_settings view). In the latter
+ * case we don't apply any of the settings, but we make all the usual validity
+ * checks, and we return the ConfigVariable list so that it can be printed out
+ * by show_all_file_settings().
+ */
+static ConfigVariable *
+ProcessConfigFileInternal(GucContext context, bool applySettings, int elevel)
+{
+ bool error = false;
+ bool applying = false;
+ const char *ConfFileWithError;
+ ConfigVariable *item,
+ *head,
+ *tail;
+ int i;
+
/* Parse the main config file into a list of option names and values */
ConfFileWithError = ConfigFileName;
head = tail = NULL;
- if (!ParseConfigFile(ConfigFileName, NULL, true, 0, elevel, &head, &tail))
+ if (!ParseConfigFile(ConfigFileName, true,
+ NULL, 0, 0, elevel,
+ &head, &tail))
{
/* Syntax error(s) detected in the file, so bail out */
error = true;
- goto cleanup_list;
+ goto bail_out;
}
/*
*/
if (DataDir)
{
- if (!ParseConfigFile(PG_AUTOCONF_FILENAME, NULL, false, 0, elevel,
+ if (!ParseConfigFile(PG_AUTOCONF_FILENAME, false,
+ NULL, 0, 0, elevel,
&head, &tail))
{
/* Syntax error(s) detected in the file, so bail out */
error = true;
ConfFileWithError = PG_AUTOCONF_FILENAME;
- goto cleanup_list;
+ goto bail_out;
}
}
else
* will be read later. OTOH, since data_directory isn't allowed in the
* PG_AUTOCONF_FILENAME file, it will never be overwritten later.
*/
- ConfigVariable *prev = NULL;
+ ConfigVariable *newlist = NULL;
- /* Prune all items except "data_directory" from the list */
- for (item = head; item;)
+ /*
+ * Prune all items except the last "data_directory" from the list.
+ */
+ for (item = head; item; item = item->next)
{
- ConfigVariable *ptr = item;
-
- item = item->next;
- if (strcmp(ptr->name, "data_directory") != 0)
- {
- if (prev == NULL)
- head = ptr->next;
- else
- prev->next = ptr->next;
- if (ptr->next == NULL)
- tail = prev;
- FreeConfigVariable(ptr);
- }
- else
- prev = ptr;
+ if (!item->ignore &&
+ strcmp(item->name, "data_directory") == 0)
+ newlist = item;
}
+ if (newlist)
+ newlist->next = NULL;
+ head = tail = newlist;
+
/*
* Quick exit if data_directory is not present in file.
*
* the config file.
*/
if (head == NULL)
- return;
+ goto bail_out;
}
/*
* same reason, we don't attempt to validate the options' values here.
*
* In addition, the GUC_IS_IN_FILE flag is set on each existing GUC
- * variable mentioned in the file.
+ * variable mentioned in the file; and we detect duplicate entries in
+ * the file and mark the earlier occurrences as ignorable.
*/
for (item = head; item; item = item->next)
{
struct config_generic *record;
+ /* Ignore anything already marked as ignorable */
+ if (item->ignore)
+ continue;
+
/*
* Try to find the variable; but do not create a custom placeholder
* if it's not there already.
if (record)
{
- /* Found, so mark it as present in file */
+ /* If it's already marked, then this is a duplicate entry */
+ if (record->status & GUC_IS_IN_FILE)
+ {
+ /*
+ * Mark the earlier occurrence(s) as dead/ignorable. We could
+ * avoid the O(N^2) behavior here with some additional state,
+ * but it seems unlikely to be worth the trouble.
+ */
+ ConfigVariable *pitem;
+
+ for (pitem = head; pitem != item; pitem = pitem->next)
+ {
+ if (!pitem->ignore &&
+ strcmp(pitem->name, item->name) == 0)
+ pitem->ignore = true;
+ }
+ }
+ /* Now mark it as present in file */
record->status |= GUC_IS_IN_FILE;
}
else if (strchr(item->name, GUC_QUALIFIER_SEPARATOR) == NULL)
errmsg("unrecognized configuration parameter \"%s\" in file \"%s\" line %u",
item->name,
item->filename, item->sourceline)));
+ item->errmsg = pstrdup("unrecognized configuration parameter");
error = true;
ConfFileWithError = item->filename;
}
- file_variables_count++;
}
/*
* any changes.
*/
if (error)
- goto cleanup_list;
+ goto bail_out;
/* Otherwise, set flag that we're beginning to apply changes */
- apply = true;
+ applying = true;
/*
* Check for variables having been removed from the config file, and
(errcode(ERRCODE_CANT_CHANGE_RUNTIME_PARAM),
errmsg("parameter \"%s\" cannot be changed without restarting the server",
gconf->name)));
+ record_config_file_error(psprintf("parameter \"%s\" cannot be changed without restarting the server",
+ gconf->name),
+ NULL, 0,
+ &head, &tail);
error = true;
continue;
}
+ /* No more to do if we're just doing show_all_file_settings() */
+ if (!applySettings)
+ continue;
+
/*
* Reset any "file" sources to "default", else set_config_option
* will not override those settings.
* potentially have PGC_S_DYNAMIC_DEFAULT or PGC_S_ENV_VAR source.
* However, there's no time to redesign it for 9.1.
*/
- if (context == PGC_SIGHUP)
+ if (context == PGC_SIGHUP && applySettings)
{
InitializeGUCOptionsFromEnvironment();
pg_timezone_abbrev_initialize();
PGC_BACKEND, PGC_S_DYNAMIC_DEFAULT);
}
- /*
- * Check if we have allocated the array yet.
- *
- * If not, allocate it based on the number of file variables we have seen.
- */
- if (!guc_file_variables)
- {
- /* For the first call */
- num_guc_file_variables = file_variables_count;
- guc_file_variables = (ConfigFileVariable *) guc_malloc(FATAL,
- num_guc_file_variables * sizeof(struct ConfigFileVariable));
- }
- else
- {
- int i;
-
- /* Free all of the previously allocated entries */
- for (i = 0; i < num_guc_file_variables; i++)
- {
- free(guc_file_variables[i].name);
- free(guc_file_variables[i].value);
- free(guc_file_variables[i].filename);
- }
-
- /* Update the global count and realloc based on the new size */
- num_guc_file_variables = file_variables_count;
- guc_file_variables = (ConfigFileVariable *) guc_realloc(FATAL,
- guc_file_variables,
- num_guc_file_variables * sizeof(struct ConfigFileVariable));
- }
-
- /*
- * Copy the settings which came from the files read into the
- * guc_file_variables array which backs the pg_show_file_settings()
- * function.
- */
- for (item = head, i = 0; item && i < num_guc_file_variables;
- item = item->next, i++)
- {
- guc_file_variables[i].name = guc_strdup(FATAL, item->name);
- guc_file_variables[i].value = guc_strdup(FATAL, item->value);
- guc_file_variables[i].filename = guc_strdup(FATAL, item->filename);
- guc_file_variables[i].sourceline = item->sourceline;
- }
-
- /* We had better have made it through the loop above to a clean ending. */
- Assert(!item && i == num_guc_file_variables);
-
/*
* Now apply the values from the config file.
*/
char *pre_value = NULL;
int scres;
+ /* Ignore anything marked as ignorable */
+ if (item->ignore)
+ continue;
+
/* In SIGHUP cases in the postmaster, we want to report changes */
- if (context == PGC_SIGHUP && !IsUnderPostmaster)
+ if (context == PGC_SIGHUP && applySettings && !IsUnderPostmaster)
{
const char *preval = GetConfigOption(item->name, true, false);
scres = set_config_option(item->name, item->value,
context, PGC_S_FILE,
- GUC_ACTION_SET, true, 0, false);
+ GUC_ACTION_SET, applySettings, 0, false);
if (scres > 0)
{
/* variable was updated, so log the change if appropriate */
(errmsg("parameter \"%s\" changed to \"%s\"",
item->name, item->value)));
}
+ item->applied = true;
}
else if (scres == 0)
{
error = true;
+ item->errmsg = pstrdup("setting could not be applied");
ConfFileWithError = item->filename;
}
- /* else no error but variable's active value was not changed */
+ else
+ {
+ /* no error, but variable's active value was not changed */
+ item->applied = true;
+ }
/*
* We should update source location unless there was an error, since
* (In the postmaster, there won't be a difference, but it does matter
* in backends.)
*/
- if (scres != 0)
+ if (scres != 0 && applySettings)
set_config_sourcefile(item->name, item->filename,
item->sourceline);
}
/* Remember when we last successfully loaded the config file. */
- PgReloadTime = GetCurrentTimestamp();
+ if (applySettings)
+ PgReloadTime = GetCurrentTimestamp();
- cleanup_list:
- if (error)
+ bail_out:
+ if (error && applySettings)
{
/* During postmaster startup, any error is fatal */
if (context == PGC_POSTMASTER)
(errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("configuration file \"%s\" contains errors",
ConfFileWithError)));
- else if (apply)
+ else if (applying)
ereport(elevel,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("configuration file \"%s\" contains errors; unaffected changes were applied",
ConfFileWithError)));
}
- /*
- * Calling FreeConfigVariables() any earlier than this can cause problems,
- * because ConfFileWithError could be pointing to a string that will be
- * freed here.
- */
- FreeConfigVariables(head);
+ /* Successful or otherwise, return the collected data list */
+ return head;
}
/*
* If "strict" is true, treat failure to open the config file as an error,
* otherwise just skip the file.
*
+ * calling_file/calling_lineno identify the source of the request.
+ * Pass NULL/0 if not recursing from an inclusion request.
+ *
* See ParseConfigFp for further details. This one merely adds opening the
* config file rather than working from a caller-supplied file descriptor,
* and absolute-ifying the path name if necessary.
*/
bool
-ParseConfigFile(const char *config_file, const char *calling_file, bool strict,
+ParseConfigFile(const char *config_file, bool strict,
+ const char *calling_file, int calling_lineno,
int depth, int elevel,
ConfigVariable **head_p,
ConfigVariable **tail_p)
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
errmsg("could not open configuration file \"%s\": maximum nesting depth exceeded",
config_file)));
+ record_config_file_error("nesting depth exceeded",
+ calling_file, calling_lineno,
+ head_p, tail_p);
return false;
}
(errcode_for_file_access(),
errmsg("could not open configuration file \"%s\": %m",
abs_path)));
+ record_config_file_error(psprintf("could not open file \"%s\"",
+ abs_path),
+ calling_file, calling_lineno,
+ head_p, tail_p);
OK = false;
}
else
return OK;
}
+/*
+ * Capture an error message in the ConfigVariable list returned by
+ * config file parsing.
+ */
+static void
+record_config_file_error(const char *errmsg,
+ const char *config_file,
+ int lineno,
+ ConfigVariable **head_p,
+ ConfigVariable **tail_p)
+{
+ ConfigVariable *item;
+
+ item = palloc(sizeof *item);
+ item->name = NULL;
+ item->value = NULL;
+ item->errmsg = pstrdup(errmsg);
+ item->filename = config_file ? pstrdup(config_file) : NULL;
+ item->sourceline = lineno;
+ item->ignore = true;
+ item->applied = false;
+ item->next = NULL;
+ if (*head_p == NULL)
+ *head_p = item;
+ else
+ (*tail_p)->next = item;
+ *tail_p = item;
+}
+
/*
* Flex fatal errors bring us here. Stash the error message and jump back to
* ParseConfigFp(). Assume all msg arguments point to string constants; this
* Input/Output parameters:
* head_p, tail_p: head and tail of linked list of name/value pairs
*
- * *head_p and *tail_p must either be initialized to NULL or valid pointers
- * to a ConfigVariable list before calling the outer recursion level. Any
- * name-value pairs read from the input file(s) will be added to the list.
+ * *head_p and *tail_p must be initialized, either to NULL or valid pointers
+ * to a ConfigVariable list, before calling the outer recursion level. Any
+ * name-value pairs read from the input file(s) will be appended to the list.
+ * Error reports will also be appended to the list, if elevel < ERROR.
*
* Returns TRUE if successful, FALSE if an error occurred. The error has
* already been ereport'd, it is only necessary for the caller to clean up
*
* Note: if elevel >= ERROR then an error will not return control to the
* caller, so there is no need to check the return value in that case.
+ *
+ * Note: this function is used to parse not only postgresql.conf, but
+ * various other configuration files that use the same "name = value"
+ * syntax. Hence, do not do anything here or in the subsidiary routines
+ * ParseConfigFile/ParseConfigDirectory that assumes we are processing
+ * GUCs specifically.
*/
bool
ParseConfigFp(FILE *fp, const char *config_file, int depth, int elevel,
*/
elog(elevel, "%s at file \"%s\" line %u",
GUC_flex_fatal_errmsg, config_file, ConfigFileLineno);
-
+ record_config_file_error(GUC_flex_fatal_errmsg,
+ config_file, ConfigFileLineno,
+ head_p, tail_p);
OK = false;
goto cleanup;
}
* An include_dir directive isn't a variable and should be
* processed immediately.
*/
- if (!ParseConfigDirectory(opt_value, config_file,
- depth + 1, elevel,
- head_p, tail_p))
+ if (!ParseConfigDirectory(opt_value,
+ config_file, ConfigFileLineno - 1,
+ depth + 1, elevel,
+ head_p, tail_p))
OK = false;
yy_switch_to_buffer(lex_buffer);
- ConfigFileLineno = save_ConfigFileLineno;
pfree(opt_name);
pfree(opt_value);
}
* An include_if_exists directive isn't a variable and should be
* processed immediately.
*/
- if (!ParseConfigFile(opt_value, config_file, false,
+ if (!ParseConfigFile(opt_value, false,
+ config_file, ConfigFileLineno - 1,
depth + 1, elevel,
head_p, tail_p))
OK = false;
* An include directive isn't a variable and should be processed
* immediately.
*/
- if (!ParseConfigFile(opt_value, config_file, true,
+ if (!ParseConfigFile(opt_value, true,
+ config_file, ConfigFileLineno - 1,
depth + 1, elevel,
head_p, tail_p))
OK = false;
item = palloc(sizeof *item);
item->name = opt_name;
item->value = opt_value;
+ item->errmsg = NULL;
item->filename = pstrdup(config_file);
item->sourceline = ConfigFileLineno-1;
+ item->ignore = false;
+ item->applied = false;
item->next = NULL;
if (*head_p == NULL)
*head_p = item;
/* report the error */
if (token == GUC_EOL || token == 0)
+ {
ereport(elevel,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("syntax error in file \"%s\" line %u, near end of line",
config_file, ConfigFileLineno - 1)));
+ record_config_file_error("syntax error",
+ config_file, ConfigFileLineno - 1,
+ head_p, tail_p);
+ }
else
+ {
ereport(elevel,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("syntax error in file \"%s\" line %u, near token \"%s\"",
config_file, ConfigFileLineno, yytext)));
+ record_config_file_error("syntax error",
+ config_file, ConfigFileLineno,
+ head_p, tail_p);
+ }
OK = false;
errorcount++;
/*
* Read and parse all config files in a subdirectory in alphabetical order
+ *
+ * includedir is the absolute or relative path to the subdirectory to scan.
+ *
+ * calling_file/calling_lineno identify the source of the request.
+ * Pass NULL/0 if not recursing from an inclusion request.
+ *
+ * See ParseConfigFp for further details.
*/
bool
ParseConfigDirectory(const char *includedir,
- const char *calling_file,
+ const char *calling_file, int calling_lineno,
int depth, int elevel,
ConfigVariable **head_p,
ConfigVariable **tail_p)
char *directory;
DIR *d;
struct dirent *de;
- char **filenames = NULL;
- int num_filenames = 0;
- int size_filenames = 0;
+ char **filenames;
+ int num_filenames;
+ int size_filenames;
bool status;
directory = AbsoluteConfigLocation(includedir, calling_file);
(errcode_for_file_access(),
errmsg("could not open configuration directory \"%s\": %m",
directory)));
+ record_config_file_error(psprintf("could not open directory \"%s\"",
+ directory),
+ calling_file, calling_lineno,
+ head_p, tail_p);
status = false;
goto cleanup;
}
* Read the directory and put the filenames in an array, so we can sort
* them prior to processing the contents.
*/
+ size_filenames = 32;
+ filenames = (char **) palloc(size_filenames * sizeof(char *));
+ num_filenames = 0;
+
while ((de = ReadDir(d, directory)) != NULL)
{
struct stat st;
{
if (!S_ISDIR(st.st_mode))
{
- /* Add file to list, increasing its size in blocks of 32 */
- if (num_filenames == size_filenames)
+ /* Add file to array, increasing its size in blocks of 32 */
+ if (num_filenames >= size_filenames)
{
size_filenames += 32;
- if (num_filenames == 0)
- /* Must initialize, repalloc won't take NULL input */
- filenames = palloc(size_filenames * sizeof(char *));
- else
- filenames = repalloc(filenames, size_filenames * sizeof(char *));
+ filenames = (char **) repalloc(filenames,
+ size_filenames * sizeof(char *));
}
filenames[num_filenames] = pstrdup(filename);
num_filenames++;
(errcode_for_file_access(),
errmsg("could not stat file \"%s\": %m",
filename)));
+ record_config_file_error(psprintf("could not stat file \"%s\"",
+ filename),
+ calling_file, calling_lineno,
+ head_p, tail_p);
status = false;
goto cleanup;
}
qsort(filenames, num_filenames, sizeof(char *), pg_qsort_strcmp);
for (i = 0; i < num_filenames; i++)
{
- if (!ParseConfigFile(filenames[i], NULL, true,
- depth, elevel, head_p, tail_p))
+ if (!ParseConfigFile(filenames[i], true,
+ calling_file, calling_lineno,
+ depth, elevel,
+ head_p, tail_p))
{
status = false;
goto cleanup;
static void
FreeConfigVariable(ConfigVariable *item)
{
- pfree(item->name);
- pfree(item->value);
- pfree(item->filename);
+ if (item->name)
+ pfree(item->name);
+ if (item->value)
+ pfree(item->value);
+ if (item->errmsg)
+ pfree(item->errmsg);
+ if (item->filename)
+ pfree(item->filename);
pfree(item);
}
static const char *show_unix_socket_permissions(void);
static const char *show_log_file_mode(void);
+/* Private functions in guc-file.l that need to be called from guc.c */
+static ConfigVariable *ProcessConfigFileInternal(GucContext context,
+ bool applySettings, int elevel);
+
/*
* Options for enum values defined in this module.
/* Current number of variables contained in the vector */
static int num_guc_variables;
-/*
- * Lookup of variables for pg_file_settings view.
- * guc_file_variables is an array of length num_guc_file_variables.
- */
-typedef struct ConfigFileVariable
-{
- char *name;
- char *value;
- char *filename;
- int sourceline;
-} ConfigFileVariable;
-static struct ConfigFileVariable *guc_file_variables;
-
-/* Number of file variables */
-static int num_guc_file_variables;
-
/* Vector capacity */
static int size_guc_variables;
item = palloc(sizeof *item);
item->name = pstrdup(name);
item->value = pstrdup(value);
+ item->errmsg = NULL;
item->filename = pstrdup(""); /* new item has no location */
item->sourceline = 0;
+ item->ignore = false;
+ item->applied = false;
item->next = NULL;
if (*head_p == NULL)
AutoConfFileName)));
/* parse it */
- ParseConfigFp(infile, AutoConfFileName, 0, LOG, &head, &tail);
+ if (!ParseConfigFp(infile, AutoConfFileName, 0, LOG, &head, &tail))
+ ereport(ERROR,
+ (errmsg("could not parse contents of file \"%s\"",
+ AutoConfFileName)));
FreeFile(infile);
}
/*
* show_all_file_settings
*
- * returns a table of all parameter settings in all configuration files
- * which includes the config file path/name, the line number, a sequence number
- * indicating when we loaded it, the parameter name, and the value it is
- * set to.
+ * Returns a table of all parameter settings in all configuration files
+ * which includes the config file pathname, the line number, a sequence number
+ * indicating the order in which the settings were encountered, the parameter
+ * name and value, a bool showing if the value could be applied, and possibly
+ * an associated error message. (For problems such as syntax errors, the
+ * parameter name/value might be NULL.)
*
* Note: no filtering is done here, instead we depend on the GRANT system
* to prevent unprivileged users from accessing this function or the view
Datum
show_all_file_settings(PG_FUNCTION_ARGS)
{
-#define NUM_PG_FILE_SETTINGS_ATTS 5
- FuncCallContext *funcctx;
+#define NUM_PG_FILE_SETTINGS_ATTS 7
+ ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
TupleDesc tupdesc;
- int call_cntr;
- int max_calls;
- AttInMetadata *attinmeta;
+ Tuplestorestate *tupstore;
+ ConfigVariable *conf;
+ int seqno;
+ MemoryContext per_query_ctx;
MemoryContext oldcontext;
- if (SRF_IS_FIRSTCALL())
- {
- funcctx = SRF_FIRSTCALL_INIT();
-
- oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
+ /* Check to see if caller supports us returning a tuplestore */
+ if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("set-valued function called in context that cannot accept a set")));
+ if (!(rsinfo->allowedModes & SFRM_Materialize))
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("materialize mode required, but it is not " \
+ "allowed in this context")));
- /*
- * need a tuple descriptor representing NUM_PG_FILE_SETTINGS_ATTS
- * columns of the appropriate types
- */
+ /* Scan the config files using current context as workspace */
+ conf = ProcessConfigFileInternal(PGC_SIGHUP, false, DEBUG3);
- tupdesc = CreateTemplateTupleDesc(NUM_PG_FILE_SETTINGS_ATTS, false);
- TupleDescInitEntry(tupdesc, (AttrNumber) 1, "sourcefile",
- TEXTOID, -1, 0);
- TupleDescInitEntry(tupdesc, (AttrNumber) 2, "sourceline",
- INT4OID, -1, 0);
- TupleDescInitEntry(tupdesc, (AttrNumber) 3, "seqno",
- INT4OID, -1, 0);
- TupleDescInitEntry(tupdesc, (AttrNumber) 4, "name",
- TEXTOID, -1, 0);
- TupleDescInitEntry(tupdesc, (AttrNumber) 5, "setting",
- TEXTOID, -1, 0);
+ /* Switch into long-lived context to construct returned data structures */
+ per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
+ oldcontext = MemoryContextSwitchTo(per_query_ctx);
- attinmeta = TupleDescGetAttInMetadata(tupdesc);
- funcctx->attinmeta = attinmeta;
- funcctx->max_calls = num_guc_file_variables;
- MemoryContextSwitchTo(oldcontext);
- }
+ /* Build a tuple descriptor for our result type */
+ tupdesc = CreateTemplateTupleDesc(NUM_PG_FILE_SETTINGS_ATTS, false);
+ TupleDescInitEntry(tupdesc, (AttrNumber) 1, "sourcefile",
+ TEXTOID, -1, 0);
+ TupleDescInitEntry(tupdesc, (AttrNumber) 2, "sourceline",
+ INT4OID, -1, 0);
+ TupleDescInitEntry(tupdesc, (AttrNumber) 3, "seqno",
+ INT4OID, -1, 0);
+ TupleDescInitEntry(tupdesc, (AttrNumber) 4, "name",
+ TEXTOID, -1, 0);
+ TupleDescInitEntry(tupdesc, (AttrNumber) 5, "setting",
+ TEXTOID, -1, 0);
+ TupleDescInitEntry(tupdesc, (AttrNumber) 6, "applied",
+ BOOLOID, -1, 0);
+ TupleDescInitEntry(tupdesc, (AttrNumber) 7, "error",
+ TEXTOID, -1, 0);
- funcctx = SRF_PERCALL_SETUP();
+ /* Build a tuplestore to return our results in */
+ tupstore = tuplestore_begin_heap(true, false, work_mem);
+ rsinfo->returnMode = SFRM_Materialize;
+ rsinfo->setResult = tupstore;
+ rsinfo->setDesc = tupdesc;
- call_cntr = funcctx->call_cntr;
- max_calls = funcctx->max_calls;
- attinmeta = funcctx->attinmeta;
+ /* The rest can be done in short-lived context */
+ MemoryContextSwitchTo(oldcontext);
- if (call_cntr < max_calls)
+ /* Process the results and create a tuplestore */
+ for (seqno = 1; conf != NULL; conf = conf->next, seqno++)
{
- char *values[NUM_PG_FILE_SETTINGS_ATTS];
- HeapTuple tuple;
- Datum result;
- ConfigFileVariable conf;
- char buffer[12]; /* must be at least 12, per pg_ltoa */
+ Datum values[NUM_PG_FILE_SETTINGS_ATTS];
+ bool nulls[NUM_PG_FILE_SETTINGS_ATTS];
- /* Check to avoid going past end of array */
- if (call_cntr > num_guc_file_variables)
- SRF_RETURN_DONE(funcctx);
-
- conf = guc_file_variables[call_cntr];
+ memset(values, 0, sizeof(values));
+ memset(nulls, 0, sizeof(nulls));
/* sourcefile */
- values[0] = conf.filename;
+ if (conf->filename)
+ values[0] = PointerGetDatum(cstring_to_text(conf->filename));
+ else
+ nulls[0] = true;
- /* sourceline */
- pg_ltoa(conf.sourceline, buffer);
- values[1] = pstrdup(buffer);
+ /* sourceline (not meaningful if no sourcefile) */
+ if (conf->filename)
+ values[1] = Int32GetDatum(conf->sourceline);
+ else
+ nulls[1] = true;
/* seqno */
- pg_ltoa(call_cntr + 1, buffer);
- values[2] = pstrdup(buffer);
+ values[2] = Int32GetDatum(seqno);
/* name */
- values[3] = conf.name;
+ if (conf->name)
+ values[3] = PointerGetDatum(cstring_to_text(conf->name));
+ else
+ nulls[3] = true;
/* setting */
- values[4] = conf.value;
+ if (conf->value)
+ values[4] = PointerGetDatum(cstring_to_text(conf->value));
+ else
+ nulls[4] = true;
- /* build a tuple */
- tuple = BuildTupleFromCStrings(attinmeta, values);
+ /* applied */
+ values[5] = BoolGetDatum(conf->applied);
- /* make the tuple into a datum */
- result = HeapTupleGetDatum(tuple);
+ /* error */
+ if (conf->errmsg)
+ values[6] = PointerGetDatum(cstring_to_text(conf->errmsg));
+ else
+ nulls[6] = true;
- SRF_RETURN_NEXT(funcctx, result);
- }
- else
- {
- SRF_RETURN_DONE(funcctx);
+ /* shove row into tuplestore */
+ tuplestore_putvalues(tupstore, tupdesc, values, nulls);
}
+
+ tuplestore_donestoring(tupstore);
+
+ return (Datum) 0;
}
static char *