diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml
index b9c8fccde43..ae58708aaea 100644
--- a/doc/src/sgml/ref/psql-ref.sgml
+++ b/doc/src/sgml/ref/psql-ref.sgml
@@ -3247,12 +3247,6 @@ bar
fail after having already displayed some rows.
-
- FETCH_COUNT is ignored if it is unset or does not
- have a positive value. It cannot be set to a value that is not
- syntactically an integer.
-
-
Although you can use any output format with this feature,
@@ -3316,10 +3310,8 @@ bar
HISTSIZE
- The maximum number of commands to store in the command history.
- If unset, at most 500 commands are stored by default.
- If set to a value that is negative or not an integer, no limit is
- applied.
+ The maximum number of commands to store in the command history
+ (default 500). If set to a negative value, no limit is applied.
@@ -3345,13 +3337,13 @@ bar
IGNOREEOF
- If unset, sending an EOF> character (usually
+ If set to 1 or less, sending an EOF> character (usually
Control>D>>)
to an interactive session of psql
- will terminate the application. If set to a numeric value,
- that many EOF> characters are ignored before the
- application terminates. If the variable is set but not to a
- numeric value, the default is 10.
+ will terminate the application. If set to a larger numeric value,
+ that many consecutive EOF> characters must be typed to
+ make an interactive session terminate. If the variable is set to a
+ non-numeric value, it is interpreted as 10.
diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c
index 53656294da4..3e3cab49418 100644
--- a/src/bin/psql/help.c
+++ b/src/bin/psql/help.c
@@ -348,9 +348,9 @@ helpVariables(unsigned short int pager)
" (default: 0=unlimited)\n"));
fprintf(output, _(" HISTCONTROL controls command history [ignorespace, ignoredups, ignoreboth]\n"));
fprintf(output, _(" HISTFILE file name used to store the command history\n"));
- fprintf(output, _(" HISTSIZE the number of commands to store in the command history\n"));
+ fprintf(output, _(" HISTSIZE max number of commands to store in the command history\n"));
fprintf(output, _(" HOST the currently connected database server host\n"));
- fprintf(output, _(" IGNOREEOF if unset, sending an EOF to interactive session terminates application\n"));
+ fprintf(output, _(" IGNOREEOF number of EOFs needed to terminate an interactive session\n"));
fprintf(output, _(" LASTOID value of the last affected OID\n"));
fprintf(output, _(" ON_ERROR_ROLLBACK if set, an error doesn't stop a transaction (uses implicit savepoints)\n"));
fprintf(output, _(" ON_ERROR_STOP stop batch execution after error\n"));
diff --git a/src/bin/psql/input.c b/src/bin/psql/input.c
index 3e3e97ad0d2..b8c9a00b099 100644
--- a/src/bin/psql/input.c
+++ b/src/bin/psql/input.c
@@ -539,10 +539,7 @@ finishInput(void)
#ifdef USE_READLINE
if (useHistory && psql_history)
{
- int hist_size;
-
- hist_size = GetVariableNum(pset.vars, "HISTSIZE", 500, -1);
- (void) saveHistory(psql_history, hist_size);
+ (void) saveHistory(psql_history, pset.histsize);
free(psql_history);
psql_history = NULL;
}
diff --git a/src/bin/psql/mainloop.c b/src/bin/psql/mainloop.c
index dc25b4babc5..6e358e2e1b8 100644
--- a/src/bin/psql/mainloop.c
+++ b/src/bin/psql/mainloop.c
@@ -162,7 +162,7 @@ MainLoop(FILE *source)
/* This tries to mimic bash's IGNOREEOF feature. */
count_eof++;
- if (count_eof < GetVariableNum(pset.vars, "IGNOREEOF", 0, 10))
+ if (count_eof < pset.ignoreeof)
{
if (!pset.quiet)
printf(_("Use \"\\q\" to leave %s.\n"), pset.progname);
diff --git a/src/bin/psql/settings.h b/src/bin/psql/settings.h
index 4c7c3b1fa37..195f5a11843 100644
--- a/src/bin/psql/settings.h
+++ b/src/bin/psql/settings.h
@@ -125,6 +125,8 @@ typedef struct _psqlSettings
bool singleline;
bool singlestep;
int fetch_count;
+ int histsize;
+ int ignoreeof;
PSQL_ECHO echo;
PSQL_ECHO_HIDDEN echo_hidden;
PSQL_ERROR_ROLLBACK on_error_rollback;
diff --git a/src/bin/psql/startup.c b/src/bin/psql/startup.c
index a3654e62722..88d686a5b74 100644
--- a/src/bin/psql/startup.c
+++ b/src/bin/psql/startup.c
@@ -774,6 +774,11 @@ showVersion(void)
* Substitute hooks and assign hooks for psql variables.
*
* This isn't an amazingly good place for them, but neither is anywhere else.
+ *
+ * By policy, every special variable that controls any psql behavior should
+ * have one or both hooks, even if they're just no-ops. This ensures that
+ * the variable will remain present in variables.c's list even when unset,
+ * which ensures that it's known to tab completion.
*/
static char *
@@ -823,16 +828,71 @@ singlestep_hook(const char *newval)
return ParseVariableBool(newval, "SINGLESTEP", &pset.singlestep);
}
+static char *
+fetch_count_substitute_hook(char *newval)
+{
+ if (newval == NULL)
+ newval = pg_strdup("0");
+ return newval;
+}
+
static bool
fetch_count_hook(const char *newval)
{
- if (newval == NULL)
- pset.fetch_count = -1; /* default value */
- else if (!ParseVariableNum(newval, "FETCH_COUNT", &pset.fetch_count))
- return false;
+ return ParseVariableNum(newval, "FETCH_COUNT", &pset.fetch_count);
+}
+
+static bool
+histfile_hook(const char *newval)
+{
+ /*
+ * Someday we might try to validate the filename, but for now, this is
+ * just a placeholder to ensure HISTFILE is known to tab completion.
+ */
return true;
}
+static char *
+histsize_substitute_hook(char *newval)
+{
+ if (newval == NULL)
+ newval = pg_strdup("500");
+ return newval;
+}
+
+static bool
+histsize_hook(const char *newval)
+{
+ return ParseVariableNum(newval, "HISTSIZE", &pset.histsize);
+}
+
+static char *
+ignoreeof_substitute_hook(char *newval)
+{
+ int dummy;
+
+ /*
+ * This tries to mimic the behavior of bash, to wit "If set, the value is
+ * the number of consecutive EOF characters which must be typed as the
+ * first characters on an input line before bash exits. If the variable
+ * exists but does not have a numeric value, or has no value, the default
+ * value is 10. If it does not exist, EOF signifies the end of input to
+ * the shell." Unlike bash, however, we insist on the stored value
+ * actually being a valid integer.
+ */
+ if (newval == NULL)
+ newval = pg_strdup("0");
+ else if (!ParseVariableNum(newval, NULL, &dummy))
+ newval = pg_strdup("10");
+ return newval;
+}
+
+static bool
+ignoreeof_hook(const char *newval)
+{
+ return ParseVariableNum(newval, "IGNOREEOF", &pset.ignoreeof);
+}
+
static char *
echo_substitute_hook(char *newval)
{
@@ -1062,8 +1122,17 @@ EstablishVariableSpace(void)
bool_substitute_hook,
singlestep_hook);
SetVariableHooks(pset.vars, "FETCH_COUNT",
- NULL,
+ fetch_count_substitute_hook,
fetch_count_hook);
+ SetVariableHooks(pset.vars, "HISTFILE",
+ NULL,
+ histfile_hook);
+ SetVariableHooks(pset.vars, "HISTSIZE",
+ histsize_substitute_hook,
+ histsize_hook);
+ SetVariableHooks(pset.vars, "IGNOREEOF",
+ ignoreeof_substitute_hook,
+ ignoreeof_hook);
SetVariableHooks(pset.vars, "ECHO",
echo_substitute_hook,
echo_hook);
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index d6fffcf42f1..6e759d0b76f 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -3775,8 +3775,9 @@ append_variable_names(char ***varnames, int *nvars,
/*
* This function supports completion with the name of a psql variable.
* The variable names can be prefixed and suffixed with additional text
- * to support quoting usages. If need_value is true, only the variables
- * that have the set values are picked up.
+ * to support quoting usages. If need_value is true, only variables
+ * that are currently set are included; otherwise, special variables
+ * (those that have hooks) are included even if currently unset.
*/
static char **
complete_from_variables(const char *text, const char *prefix, const char *suffix,
@@ -3789,33 +3790,12 @@ complete_from_variables(const char *text, const char *prefix, const char *suffix
int i;
struct _variable *ptr;
- static const char *const known_varnames[] = {
- "AUTOCOMMIT", "COMP_KEYWORD_CASE", "DBNAME", "ECHO", "ECHO_HIDDEN",
- "ENCODING", "FETCH_COUNT", "HISTCONTROL", "HISTFILE", "HISTSIZE",
- "HOST", "IGNOREEOF", "LASTOID", "ON_ERROR_ROLLBACK", "ON_ERROR_STOP",
- "PORT", "PROMPT1", "PROMPT2", "PROMPT3", "QUIET",
- "SHOW_CONTEXT", "SINGLELINE", "SINGLESTEP",
- "USER", "VERBOSITY", NULL
- };
-
varnames = (char **) pg_malloc((maxvars + 1) * sizeof(char *));
- if (!need_value)
- {
- for (i = 0; known_varnames[i] && nvars < maxvars; i++)
- append_variable_names(&varnames, &nvars, &maxvars,
- known_varnames[i], prefix, suffix);
- }
-
for (ptr = pset.vars->next; ptr; ptr = ptr->next)
{
if (need_value && !(ptr->value))
continue;
- for (i = 0; known_varnames[i]; i++) /* remove duplicate entry */
- {
- if (strcmp(ptr->name, known_varnames[i]) == 0)
- continue;
- }
append_variable_names(&varnames, &nvars, &maxvars, ptr->name,
prefix, suffix);
}
diff --git a/src/bin/psql/variables.c b/src/bin/psql/variables.c
index 9ca100095f6..d9d07631a59 100644
--- a/src/bin/psql/variables.c
+++ b/src/bin/psql/variables.c
@@ -179,31 +179,6 @@ ParseVariableNum(const char *value, const char *name, int *result)
}
}
-/*
- * Read integer value of the numeric variable named "name".
- *
- * Return defaultval if it is not set, or faultval if its value is not a
- * valid integer. (No error message is issued.)
- */
-int
-GetVariableNum(VariableSpace space,
- const char *name,
- int defaultval,
- int faultval)
-{
- const char *val;
- int result;
-
- val = GetVariable(space, name);
- if (!val)
- return defaultval;
-
- if (ParseVariableNum(val, NULL, &result))
- return result;
- else
- return faultval;
-}
-
/*
* Print values of all variables.
*/
diff --git a/src/bin/psql/variables.h b/src/bin/psql/variables.h
index 84be7805098..19257937c7c 100644
--- a/src/bin/psql/variables.h
+++ b/src/bin/psql/variables.h
@@ -81,11 +81,6 @@ bool ParseVariableBool(const char *value, const char *name,
bool ParseVariableNum(const char *value, const char *name,
int *result);
-int GetVariableNum(VariableSpace space,
- const char *name,
- int defaultval,
- int faultval);
-
void PrintVariables(VariableSpace space);
bool SetVariable(VariableSpace space, const char *name, const char *value);