mirror of
https://github.com/postgres/postgres.git
synced 2025-04-25 21:42:33 +03:00
Make psql reject attempts to set special variables to invalid values.
Previously, if the user set a special variable such as ECHO to an unrecognized value, psql would bleat but store the new value anyway, and then fall back to a default setting for the behavior controlled by the variable. This was agreed to be a not particularly good idea. With this patch, invalid values result in an error message and no change in state. (But this applies only to variables that affect psql's behavior; purely informational variables such as ENCODING can still be set to random values.) To do this, modify the API for psql's assign-hook functions so that they can return an OK/not OK result, and give them the responsibility for printing error messages when they reject a value. Adjust the APIs for ParseVariableBool and ParseVariableNum to support the new behavior conveniently. In passing, document the variable VERSION, which had somehow escaped that. And improve the quite-inadequate commenting in psql/variables.c. Daniel Vérité, reviewed by Rahila Syed, some further tweaking by me Discussion: https://postgr.es/m/7356e741-fa59-4146-a8eb-cf95fd6b21fb@mm
This commit is contained in:
parent
46aae5949f
commit
511ae628f3
@ -3078,10 +3078,8 @@ bar
|
|||||||
by <application>psql</application>. They represent certain option
|
by <application>psql</application>. They represent certain option
|
||||||
settings that can be changed at run time by altering the value of
|
settings that can be changed at run time by altering the value of
|
||||||
the variable, or in some cases represent changeable state of
|
the variable, or in some cases represent changeable state of
|
||||||
<application>psql</application>. Although
|
<application>psql</application>.
|
||||||
you can use these variables for other purposes, this is not
|
By convention, all specially treated variables' names
|
||||||
recommended, as the program behavior might grow really strange
|
|
||||||
really quickly. By convention, all specially treated variables' names
|
|
||||||
consist of all upper-case ASCII letters (and possibly digits and
|
consist of all upper-case ASCII letters (and possibly digits and
|
||||||
underscores). To ensure maximum compatibility in the future, avoid
|
underscores). To ensure maximum compatibility in the future, avoid
|
||||||
using such variable names for your own purposes. A list of all specially
|
using such variable names for your own purposes. A list of all specially
|
||||||
@ -3170,12 +3168,11 @@ bar
|
|||||||
start-up, use the switch <option>-a</option>. If set to
|
start-up, use the switch <option>-a</option>. If set to
|
||||||
<literal>queries</literal>,
|
<literal>queries</literal>,
|
||||||
<application>psql</application> prints each query to standard output
|
<application>psql</application> prints each query to standard output
|
||||||
as it is sent to the server. The switch for this is
|
as it is sent to the server. The switch to select this behavior is
|
||||||
<option>-e</option>. If set to <literal>errors</literal>, then only
|
<option>-e</option>. If set to <literal>errors</literal>, then only
|
||||||
failed queries are displayed on standard error output. The switch
|
failed queries are displayed on standard error output. The switch
|
||||||
for this is <option>-b</option>. If unset, or if set to
|
for this behavior is <option>-b</option>. If unset, or if set to
|
||||||
<literal>none</literal> (or any other value than those above) then
|
<literal>none</literal>, then no queries are displayed.
|
||||||
no queries are displayed.
|
|
||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
@ -3201,6 +3198,9 @@ bar
|
|||||||
<listitem>
|
<listitem>
|
||||||
<para>
|
<para>
|
||||||
The current client character set encoding.
|
The current client character set encoding.
|
||||||
|
This is set every time you connect to a database (including
|
||||||
|
program start-up), and when you change the encoding
|
||||||
|
with <literal>\encoding</>, but it can be unset.
|
||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
@ -3241,9 +3241,8 @@ bar
|
|||||||
list. If set to a value of <literal>ignoredups</literal>, lines
|
list. If set to a value of <literal>ignoredups</literal>, lines
|
||||||
matching the previous history line are not entered. A value of
|
matching the previous history line are not entered. A value of
|
||||||
<literal>ignoreboth</literal> combines the two options. If
|
<literal>ignoreboth</literal> combines the two options. If
|
||||||
unset, or if set to <literal>none</literal> (or any other value
|
unset, or if set to <literal>none</literal> (the default), all lines
|
||||||
than those above), all lines read in interactive mode are
|
read in interactive mode are saved on the history list.
|
||||||
saved on the history list.
|
|
||||||
</para>
|
</para>
|
||||||
<note>
|
<note>
|
||||||
<para>
|
<para>
|
||||||
@ -3312,7 +3311,7 @@ bar
|
|||||||
to an interactive session of <application>psql</application>
|
to an interactive session of <application>psql</application>
|
||||||
will terminate the application. If set to a numeric value,
|
will terminate the application. If set to a numeric value,
|
||||||
that many <acronym>EOF</> characters are ignored before the
|
that many <acronym>EOF</> characters are ignored before the
|
||||||
application terminates. If the variable is set but has no
|
application terminates. If the variable is set but not to a
|
||||||
numeric value, the default is 10.
|
numeric value, the default is 10.
|
||||||
</para>
|
</para>
|
||||||
<note>
|
<note>
|
||||||
@ -3477,6 +3476,16 @@ bar
|
|||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><varname>VERSION</varname></term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
This variable is set at program start-up to
|
||||||
|
reflect <application>psql</>'s version. It can be unset or changed.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
</variablelist>
|
</variablelist>
|
||||||
|
|
||||||
</refsect3>
|
</refsect3>
|
||||||
|
@ -248,31 +248,37 @@ exec_command(const char *cmd,
|
|||||||
*opt2,
|
*opt2,
|
||||||
*opt3,
|
*opt3,
|
||||||
*opt4;
|
*opt4;
|
||||||
enum trivalue reuse_previous;
|
enum trivalue reuse_previous = TRI_DEFAULT;
|
||||||
|
|
||||||
opt1 = read_connect_arg(scan_state);
|
opt1 = read_connect_arg(scan_state);
|
||||||
if (opt1 != NULL && strncmp(opt1, prefix, sizeof(prefix) - 1) == 0)
|
if (opt1 != NULL && strncmp(opt1, prefix, sizeof(prefix) - 1) == 0)
|
||||||
{
|
{
|
||||||
reuse_previous =
|
bool on_off;
|
||||||
ParseVariableBool(opt1 + sizeof(prefix) - 1, prefix) ?
|
|
||||||
TRI_YES : TRI_NO;
|
|
||||||
|
|
||||||
free(opt1);
|
success = ParseVariableBool(opt1 + sizeof(prefix) - 1,
|
||||||
opt1 = read_connect_arg(scan_state);
|
"-reuse-previous",
|
||||||
|
&on_off);
|
||||||
|
if (success)
|
||||||
|
{
|
||||||
|
reuse_previous = on_off ? TRI_YES : TRI_NO;
|
||||||
|
free(opt1);
|
||||||
|
opt1 = read_connect_arg(scan_state);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
reuse_previous = TRI_DEFAULT;
|
|
||||||
|
|
||||||
opt2 = read_connect_arg(scan_state);
|
if (success) /* give up if reuse_previous was invalid */
|
||||||
opt3 = read_connect_arg(scan_state);
|
{
|
||||||
opt4 = read_connect_arg(scan_state);
|
opt2 = read_connect_arg(scan_state);
|
||||||
|
opt3 = read_connect_arg(scan_state);
|
||||||
|
opt4 = read_connect_arg(scan_state);
|
||||||
|
|
||||||
success = do_connect(reuse_previous, opt1, opt2, opt3, opt4);
|
success = do_connect(reuse_previous, opt1, opt2, opt3, opt4);
|
||||||
|
|
||||||
|
free(opt2);
|
||||||
|
free(opt3);
|
||||||
|
free(opt4);
|
||||||
|
}
|
||||||
free(opt1);
|
free(opt1);
|
||||||
free(opt2);
|
|
||||||
free(opt3);
|
|
||||||
free(opt4);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* \cd */
|
/* \cd */
|
||||||
@ -1208,10 +1214,7 @@ exec_command(const char *cmd,
|
|||||||
|
|
||||||
if (result &&
|
if (result &&
|
||||||
!SetVariable(pset.vars, opt, result))
|
!SetVariable(pset.vars, opt, result))
|
||||||
{
|
|
||||||
psql_error("\\%s: error while setting variable\n", cmd);
|
|
||||||
success = false;
|
success = false;
|
||||||
}
|
|
||||||
|
|
||||||
if (result)
|
if (result)
|
||||||
free(result);
|
free(result);
|
||||||
@ -1325,10 +1328,8 @@ exec_command(const char *cmd,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!SetVariable(pset.vars, opt0, newval))
|
if (!SetVariable(pset.vars, opt0, newval))
|
||||||
{
|
|
||||||
psql_error("\\%s: error while setting variable\n", cmd);
|
|
||||||
success = false;
|
success = false;
|
||||||
}
|
|
||||||
free(newval);
|
free(newval);
|
||||||
}
|
}
|
||||||
free(opt0);
|
free(opt0);
|
||||||
@ -1564,7 +1565,7 @@ exec_command(const char *cmd,
|
|||||||
OT_NORMAL, NULL, false);
|
OT_NORMAL, NULL, false);
|
||||||
|
|
||||||
if (opt)
|
if (opt)
|
||||||
pset.timing = ParseVariableBool(opt, "\\timing");
|
success = ParseVariableBool(opt, "\\timing", &pset.timing);
|
||||||
else
|
else
|
||||||
pset.timing = !pset.timing;
|
pset.timing = !pset.timing;
|
||||||
if (!pset.quiet)
|
if (!pset.quiet)
|
||||||
@ -1589,10 +1590,8 @@ exec_command(const char *cmd,
|
|||||||
success = false;
|
success = false;
|
||||||
}
|
}
|
||||||
else if (!SetVariable(pset.vars, opt, NULL))
|
else if (!SetVariable(pset.vars, opt, NULL))
|
||||||
{
|
|
||||||
psql_error("\\%s: error while setting variable\n", cmd);
|
|
||||||
success = false;
|
success = false;
|
||||||
}
|
|
||||||
free(opt);
|
free(opt);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2593,7 +2592,6 @@ do_pset(const char *param, const char *value, printQueryOpt *popt, bool quiet)
|
|||||||
psql_error("\\pset: allowed formats are unaligned, aligned, wrapped, html, asciidoc, latex, latex-longtable, troff-ms\n");
|
psql_error("\\pset: allowed formats are unaligned, aligned, wrapped, html, asciidoc, latex, latex-longtable, troff-ms\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* set table line style */
|
/* set table line style */
|
||||||
@ -2612,7 +2610,6 @@ do_pset(const char *param, const char *value, printQueryOpt *popt, bool quiet)
|
|||||||
psql_error("\\pset: allowed line styles are ascii, old-ascii, unicode\n");
|
psql_error("\\pset: allowed line styles are ascii, old-ascii, unicode\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* set unicode border line style */
|
/* set unicode border line style */
|
||||||
@ -2665,7 +2662,6 @@ do_pset(const char *param, const char *value, printQueryOpt *popt, bool quiet)
|
|||||||
{
|
{
|
||||||
if (value)
|
if (value)
|
||||||
popt->topt.border = atoi(value);
|
popt->topt.border = atoi(value);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* set expanded/vertical mode */
|
/* set expanded/vertical mode */
|
||||||
@ -2676,7 +2672,17 @@ do_pset(const char *param, const char *value, printQueryOpt *popt, bool quiet)
|
|||||||
if (value && pg_strcasecmp(value, "auto") == 0)
|
if (value && pg_strcasecmp(value, "auto") == 0)
|
||||||
popt->topt.expanded = 2;
|
popt->topt.expanded = 2;
|
||||||
else if (value)
|
else if (value)
|
||||||
popt->topt.expanded = ParseVariableBool(value, param);
|
{
|
||||||
|
bool on_off;
|
||||||
|
|
||||||
|
if (ParseVariableBool(value, NULL, &on_off))
|
||||||
|
popt->topt.expanded = on_off ? 1 : 0;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
PsqlVarEnumError(param, value, "on, off, auto");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
popt->topt.expanded = !popt->topt.expanded;
|
popt->topt.expanded = !popt->topt.expanded;
|
||||||
}
|
}
|
||||||
@ -2685,7 +2691,7 @@ do_pset(const char *param, const char *value, printQueryOpt *popt, bool quiet)
|
|||||||
else if (strcmp(param, "numericlocale") == 0)
|
else if (strcmp(param, "numericlocale") == 0)
|
||||||
{
|
{
|
||||||
if (value)
|
if (value)
|
||||||
popt->topt.numericLocale = ParseVariableBool(value, param);
|
return ParseVariableBool(value, param, &popt->topt.numericLocale);
|
||||||
else
|
else
|
||||||
popt->topt.numericLocale = !popt->topt.numericLocale;
|
popt->topt.numericLocale = !popt->topt.numericLocale;
|
||||||
}
|
}
|
||||||
@ -2740,7 +2746,7 @@ do_pset(const char *param, const char *value, printQueryOpt *popt, bool quiet)
|
|||||||
else if (strcmp(param, "t") == 0 || strcmp(param, "tuples_only") == 0)
|
else if (strcmp(param, "t") == 0 || strcmp(param, "tuples_only") == 0)
|
||||||
{
|
{
|
||||||
if (value)
|
if (value)
|
||||||
popt->topt.tuples_only = ParseVariableBool(value, param);
|
return ParseVariableBool(value, param, &popt->topt.tuples_only);
|
||||||
else
|
else
|
||||||
popt->topt.tuples_only = !popt->topt.tuples_only;
|
popt->topt.tuples_only = !popt->topt.tuples_only;
|
||||||
}
|
}
|
||||||
@ -2772,10 +2778,14 @@ do_pset(const char *param, const char *value, printQueryOpt *popt, bool quiet)
|
|||||||
popt->topt.pager = 2;
|
popt->topt.pager = 2;
|
||||||
else if (value)
|
else if (value)
|
||||||
{
|
{
|
||||||
if (ParseVariableBool(value, param))
|
bool on_off;
|
||||||
popt->topt.pager = 1;
|
|
||||||
else
|
if (!ParseVariableBool(value, NULL, &on_off))
|
||||||
popt->topt.pager = 0;
|
{
|
||||||
|
PsqlVarEnumError(param, value, "on, off, always");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
popt->topt.pager = on_off ? 1 : 0;
|
||||||
}
|
}
|
||||||
else if (popt->topt.pager == 1)
|
else if (popt->topt.pager == 1)
|
||||||
popt->topt.pager = 0;
|
popt->topt.pager = 0;
|
||||||
@ -2794,7 +2804,7 @@ do_pset(const char *param, const char *value, printQueryOpt *popt, bool quiet)
|
|||||||
else if (strcmp(param, "footer") == 0)
|
else if (strcmp(param, "footer") == 0)
|
||||||
{
|
{
|
||||||
if (value)
|
if (value)
|
||||||
popt->topt.default_footer = ParseVariableBool(value, param);
|
return ParseVariableBool(value, param, &popt->topt.default_footer);
|
||||||
else
|
else
|
||||||
popt->topt.default_footer = !popt->topt.default_footer;
|
popt->topt.default_footer = !popt->topt.default_footer;
|
||||||
}
|
}
|
||||||
|
@ -841,7 +841,6 @@ StoreQueryTuple(const PGresult *result)
|
|||||||
|
|
||||||
if (!SetVariable(pset.vars, varname, value))
|
if (!SetVariable(pset.vars, varname, value))
|
||||||
{
|
{
|
||||||
psql_error("could not set variable \"%s\"\n", varname);
|
|
||||||
free(varname);
|
free(varname);
|
||||||
success = false;
|
success = false;
|
||||||
break;
|
break;
|
||||||
|
@ -541,7 +541,7 @@ finishInput(void)
|
|||||||
{
|
{
|
||||||
int hist_size;
|
int hist_size;
|
||||||
|
|
||||||
hist_size = GetVariableNum(pset.vars, "HISTSIZE", 500, -1, true);
|
hist_size = GetVariableNum(pset.vars, "HISTSIZE", 500, -1);
|
||||||
(void) saveHistory(psql_history, hist_size);
|
(void) saveHistory(psql_history, hist_size);
|
||||||
free(psql_history);
|
free(psql_history);
|
||||||
psql_history = NULL;
|
psql_history = NULL;
|
||||||
|
@ -162,7 +162,7 @@ MainLoop(FILE *source)
|
|||||||
/* This tries to mimic bash's IGNOREEOF feature. */
|
/* This tries to mimic bash's IGNOREEOF feature. */
|
||||||
count_eof++;
|
count_eof++;
|
||||||
|
|
||||||
if (count_eof < GetVariableNum(pset.vars, "IGNOREEOF", 0, 10, false))
|
if (count_eof < GetVariableNum(pset.vars, "IGNOREEOF", 0, 10))
|
||||||
{
|
{
|
||||||
if (!pset.quiet)
|
if (!pset.quiet)
|
||||||
printf(_("Use \"\\q\" to leave %s.\n"), pset.progname);
|
printf(_("Use \"\\q\" to leave %s.\n"), pset.progname);
|
||||||
|
@ -588,11 +588,7 @@ parse_psql_options(int argc, char *argv[], struct adhoc_opts * options)
|
|||||||
{
|
{
|
||||||
*equal_loc = '\0';
|
*equal_loc = '\0';
|
||||||
if (!SetVariable(pset.vars, value, equal_loc + 1))
|
if (!SetVariable(pset.vars, value, equal_loc + 1))
|
||||||
{
|
|
||||||
fprintf(stderr, _("%s: could not set variable \"%s\"\n"),
|
|
||||||
pset.progname, value);
|
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
free(value);
|
free(value);
|
||||||
@ -786,43 +782,47 @@ showVersion(void)
|
|||||||
* This isn't an amazingly good place for them, but neither is anywhere else.
|
* This isn't an amazingly good place for them, but neither is anywhere else.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static void
|
static bool
|
||||||
autocommit_hook(const char *newval)
|
autocommit_hook(const char *newval)
|
||||||
{
|
{
|
||||||
pset.autocommit = ParseVariableBool(newval, "AUTOCOMMIT");
|
return ParseVariableBool(newval, "AUTOCOMMIT", &pset.autocommit);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static bool
|
||||||
on_error_stop_hook(const char *newval)
|
on_error_stop_hook(const char *newval)
|
||||||
{
|
{
|
||||||
pset.on_error_stop = ParseVariableBool(newval, "ON_ERROR_STOP");
|
return ParseVariableBool(newval, "ON_ERROR_STOP", &pset.on_error_stop);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static bool
|
||||||
quiet_hook(const char *newval)
|
quiet_hook(const char *newval)
|
||||||
{
|
{
|
||||||
pset.quiet = ParseVariableBool(newval, "QUIET");
|
return ParseVariableBool(newval, "QUIET", &pset.quiet);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static bool
|
||||||
singleline_hook(const char *newval)
|
singleline_hook(const char *newval)
|
||||||
{
|
{
|
||||||
pset.singleline = ParseVariableBool(newval, "SINGLELINE");
|
return ParseVariableBool(newval, "SINGLELINE", &pset.singleline);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static bool
|
||||||
singlestep_hook(const char *newval)
|
singlestep_hook(const char *newval)
|
||||||
{
|
{
|
||||||
pset.singlestep = ParseVariableBool(newval, "SINGLESTEP");
|
return ParseVariableBool(newval, "SINGLESTEP", &pset.singlestep);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static bool
|
||||||
fetch_count_hook(const char *newval)
|
fetch_count_hook(const char *newval)
|
||||||
{
|
{
|
||||||
pset.fetch_count = ParseVariableNum(newval, -1, -1, false);
|
if (newval == NULL)
|
||||||
|
pset.fetch_count = -1; /* default value */
|
||||||
|
else if (!ParseVariableNum(newval, "FETCH_COUNT", &pset.fetch_count))
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static bool
|
||||||
echo_hook(const char *newval)
|
echo_hook(const char *newval)
|
||||||
{
|
{
|
||||||
if (newval == NULL)
|
if (newval == NULL)
|
||||||
@ -837,39 +837,57 @@ echo_hook(const char *newval)
|
|||||||
pset.echo = PSQL_ECHO_NONE;
|
pset.echo = PSQL_ECHO_NONE;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
psql_error("unrecognized value \"%s\" for \"%s\"; assuming \"%s\"\n",
|
PsqlVarEnumError("ECHO", newval, "none, errors, queries, all");
|
||||||
newval, "ECHO", "none");
|
return false;
|
||||||
pset.echo = PSQL_ECHO_NONE;
|
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static bool
|
||||||
echo_hidden_hook(const char *newval)
|
echo_hidden_hook(const char *newval)
|
||||||
{
|
{
|
||||||
if (newval == NULL)
|
if (newval == NULL)
|
||||||
pset.echo_hidden = PSQL_ECHO_HIDDEN_OFF;
|
pset.echo_hidden = PSQL_ECHO_HIDDEN_OFF;
|
||||||
else if (pg_strcasecmp(newval, "noexec") == 0)
|
else if (pg_strcasecmp(newval, "noexec") == 0)
|
||||||
pset.echo_hidden = PSQL_ECHO_HIDDEN_NOEXEC;
|
pset.echo_hidden = PSQL_ECHO_HIDDEN_NOEXEC;
|
||||||
else if (ParseVariableBool(newval, "ECHO_HIDDEN"))
|
else
|
||||||
pset.echo_hidden = PSQL_ECHO_HIDDEN_ON;
|
{
|
||||||
else /* ParseVariableBool printed msg if needed */
|
bool on_off;
|
||||||
pset.echo_hidden = PSQL_ECHO_HIDDEN_OFF;
|
|
||||||
|
if (ParseVariableBool(newval, NULL, &on_off))
|
||||||
|
pset.echo_hidden = on_off ? PSQL_ECHO_HIDDEN_ON : PSQL_ECHO_HIDDEN_OFF;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
PsqlVarEnumError("ECHO_HIDDEN", newval, "on, off, noexec");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static bool
|
||||||
on_error_rollback_hook(const char *newval)
|
on_error_rollback_hook(const char *newval)
|
||||||
{
|
{
|
||||||
if (newval == NULL)
|
if (newval == NULL)
|
||||||
pset.on_error_rollback = PSQL_ERROR_ROLLBACK_OFF;
|
pset.on_error_rollback = PSQL_ERROR_ROLLBACK_OFF;
|
||||||
else if (pg_strcasecmp(newval, "interactive") == 0)
|
else if (pg_strcasecmp(newval, "interactive") == 0)
|
||||||
pset.on_error_rollback = PSQL_ERROR_ROLLBACK_INTERACTIVE;
|
pset.on_error_rollback = PSQL_ERROR_ROLLBACK_INTERACTIVE;
|
||||||
else if (ParseVariableBool(newval, "ON_ERROR_ROLLBACK"))
|
else
|
||||||
pset.on_error_rollback = PSQL_ERROR_ROLLBACK_ON;
|
{
|
||||||
else /* ParseVariableBool printed msg if needed */
|
bool on_off;
|
||||||
pset.on_error_rollback = PSQL_ERROR_ROLLBACK_OFF;
|
|
||||||
|
if (ParseVariableBool(newval, NULL, &on_off))
|
||||||
|
pset.on_error_rollback = on_off ? PSQL_ERROR_ROLLBACK_ON : PSQL_ERROR_ROLLBACK_OFF;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
PsqlVarEnumError("ON_ERROR_ROLLBACK", newval, "on, off, interactive");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static bool
|
||||||
comp_keyword_case_hook(const char *newval)
|
comp_keyword_case_hook(const char *newval)
|
||||||
{
|
{
|
||||||
if (newval == NULL)
|
if (newval == NULL)
|
||||||
@ -884,13 +902,14 @@ comp_keyword_case_hook(const char *newval)
|
|||||||
pset.comp_case = PSQL_COMP_CASE_LOWER;
|
pset.comp_case = PSQL_COMP_CASE_LOWER;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
psql_error("unrecognized value \"%s\" for \"%s\"; assuming \"%s\"\n",
|
PsqlVarEnumError("COMP_KEYWORD_CASE", newval,
|
||||||
newval, "COMP_KEYWORD_CASE", "preserve-upper");
|
"lower, upper, preserve-lower, preserve-upper");
|
||||||
pset.comp_case = PSQL_COMP_CASE_PRESERVE_UPPER;
|
return false;
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static bool
|
||||||
histcontrol_hook(const char *newval)
|
histcontrol_hook(const char *newval)
|
||||||
{
|
{
|
||||||
if (newval == NULL)
|
if (newval == NULL)
|
||||||
@ -905,31 +924,35 @@ histcontrol_hook(const char *newval)
|
|||||||
pset.histcontrol = hctl_none;
|
pset.histcontrol = hctl_none;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
psql_error("unrecognized value \"%s\" for \"%s\"; assuming \"%s\"\n",
|
PsqlVarEnumError("HISTCONTROL", newval,
|
||||||
newval, "HISTCONTROL", "none");
|
"none, ignorespace, ignoredups, ignoreboth");
|
||||||
pset.histcontrol = hctl_none;
|
return false;
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static bool
|
||||||
prompt1_hook(const char *newval)
|
prompt1_hook(const char *newval)
|
||||||
{
|
{
|
||||||
pset.prompt1 = newval ? newval : "";
|
pset.prompt1 = newval ? newval : "";
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static bool
|
||||||
prompt2_hook(const char *newval)
|
prompt2_hook(const char *newval)
|
||||||
{
|
{
|
||||||
pset.prompt2 = newval ? newval : "";
|
pset.prompt2 = newval ? newval : "";
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static bool
|
||||||
prompt3_hook(const char *newval)
|
prompt3_hook(const char *newval)
|
||||||
{
|
{
|
||||||
pset.prompt3 = newval ? newval : "";
|
pset.prompt3 = newval ? newval : "";
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static bool
|
||||||
verbosity_hook(const char *newval)
|
verbosity_hook(const char *newval)
|
||||||
{
|
{
|
||||||
if (newval == NULL)
|
if (newval == NULL)
|
||||||
@ -942,16 +965,16 @@ verbosity_hook(const char *newval)
|
|||||||
pset.verbosity = PQERRORS_VERBOSE;
|
pset.verbosity = PQERRORS_VERBOSE;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
psql_error("unrecognized value \"%s\" for \"%s\"; assuming \"%s\"\n",
|
PsqlVarEnumError("VERBOSITY", newval, "default, terse, verbose");
|
||||||
newval, "VERBOSITY", "default");
|
return false;
|
||||||
pset.verbosity = PQERRORS_DEFAULT;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pset.db)
|
if (pset.db)
|
||||||
PQsetErrorVerbosity(pset.db, pset.verbosity);
|
PQsetErrorVerbosity(pset.db, pset.verbosity);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static bool
|
||||||
show_context_hook(const char *newval)
|
show_context_hook(const char *newval)
|
||||||
{
|
{
|
||||||
if (newval == NULL)
|
if (newval == NULL)
|
||||||
@ -964,13 +987,13 @@ show_context_hook(const char *newval)
|
|||||||
pset.show_context = PQSHOW_CONTEXT_ALWAYS;
|
pset.show_context = PQSHOW_CONTEXT_ALWAYS;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
psql_error("unrecognized value \"%s\" for \"%s\"; assuming \"%s\"\n",
|
PsqlVarEnumError("SHOW_CONTEXT", newval, "never, errors, always");
|
||||||
newval, "SHOW_CONTEXT", "errors");
|
return false;
|
||||||
pset.show_context = PQSHOW_CONTEXT_ERRORS;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pset.db)
|
if (pset.db)
|
||||||
PQsetErrorContextVisibility(pset.db, pset.show_context);
|
PQsetErrorContextVisibility(pset.db, pset.show_context);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -58,6 +58,11 @@ CreateVariableSpace(void)
|
|||||||
return ptr;
|
return ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get string value of variable, or NULL if it's not defined.
|
||||||
|
*
|
||||||
|
* Note: result is valid until variable is next assigned to.
|
||||||
|
*/
|
||||||
const char *
|
const char *
|
||||||
GetVariable(VariableSpace space, const char *name)
|
GetVariable(VariableSpace space, const char *name)
|
||||||
{
|
{
|
||||||
@ -79,94 +84,121 @@ GetVariable(VariableSpace space, const char *name)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Try to interpret "value" as boolean value.
|
* Try to interpret "value" as a boolean value, and if successful,
|
||||||
|
* store it in *result. Otherwise don't clobber *result.
|
||||||
*
|
*
|
||||||
* Valid values are: true, false, yes, no, on, off, 1, 0; as well as unique
|
* Valid values are: true, false, yes, no, on, off, 1, 0; as well as unique
|
||||||
* prefixes thereof.
|
* prefixes thereof.
|
||||||
*
|
*
|
||||||
* "name" is the name of the variable we're assigning to, to use in error
|
* "name" is the name of the variable we're assigning to, to use in error
|
||||||
* report if any. Pass name == NULL to suppress the error report.
|
* report if any. Pass name == NULL to suppress the error report.
|
||||||
|
*
|
||||||
|
* Return true when "value" is syntactically valid, false otherwise.
|
||||||
*/
|
*/
|
||||||
bool
|
bool
|
||||||
ParseVariableBool(const char *value, const char *name)
|
ParseVariableBool(const char *value, const char *name, bool *result)
|
||||||
{
|
{
|
||||||
size_t len;
|
size_t len;
|
||||||
|
bool valid = true;
|
||||||
|
|
||||||
if (value == NULL)
|
if (value == NULL)
|
||||||
return false; /* not set -> assume "off" */
|
{
|
||||||
|
*result = false; /* not set -> assume "off" */
|
||||||
|
return valid;
|
||||||
|
}
|
||||||
|
|
||||||
len = strlen(value);
|
len = strlen(value);
|
||||||
|
|
||||||
if (pg_strncasecmp(value, "true", len) == 0)
|
if (len > 0 && pg_strncasecmp(value, "true", len) == 0)
|
||||||
return true;
|
*result = true;
|
||||||
else if (pg_strncasecmp(value, "false", len) == 0)
|
else if (len > 0 && pg_strncasecmp(value, "false", len) == 0)
|
||||||
return false;
|
*result = false;
|
||||||
else if (pg_strncasecmp(value, "yes", len) == 0)
|
else if (len > 0 && pg_strncasecmp(value, "yes", len) == 0)
|
||||||
return true;
|
*result = true;
|
||||||
else if (pg_strncasecmp(value, "no", len) == 0)
|
else if (len > 0 && pg_strncasecmp(value, "no", len) == 0)
|
||||||
return false;
|
*result = false;
|
||||||
/* 'o' is not unique enough */
|
/* 'o' is not unique enough */
|
||||||
else if (pg_strncasecmp(value, "on", (len > 2 ? len : 2)) == 0)
|
else if (pg_strncasecmp(value, "on", (len > 2 ? len : 2)) == 0)
|
||||||
return true;
|
*result = true;
|
||||||
else if (pg_strncasecmp(value, "off", (len > 2 ? len : 2)) == 0)
|
else if (pg_strncasecmp(value, "off", (len > 2 ? len : 2)) == 0)
|
||||||
return false;
|
*result = false;
|
||||||
else if (pg_strcasecmp(value, "1") == 0)
|
else if (pg_strcasecmp(value, "1") == 0)
|
||||||
return true;
|
*result = true;
|
||||||
else if (pg_strcasecmp(value, "0") == 0)
|
else if (pg_strcasecmp(value, "0") == 0)
|
||||||
return false;
|
*result = false;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* NULL is treated as false, so a non-matching value is 'true' */
|
/* string is not recognized; don't clobber *result */
|
||||||
if (name)
|
if (name)
|
||||||
psql_error("unrecognized value \"%s\" for \"%s\"; assuming \"%s\"\n",
|
psql_error("unrecognized value \"%s\" for \"%s\": boolean expected\n",
|
||||||
value, name, "on");
|
value, name);
|
||||||
return true;
|
valid = false;
|
||||||
}
|
}
|
||||||
|
return valid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Read numeric variable, or defaultval if it is not set, or faultval if its
|
* Try to interpret "value" as an integer value, and if successful,
|
||||||
* value is not a valid numeric string. If allowtrail is false, this will
|
* store it in *result. Otherwise don't clobber *result.
|
||||||
* include the case where there are trailing characters after the number.
|
*
|
||||||
|
* "name" is the name of the variable we're assigning to, to use in error
|
||||||
|
* report if any. Pass name == NULL to suppress the error report.
|
||||||
|
*
|
||||||
|
* Return true when "value" is syntactically valid, false otherwise.
|
||||||
*/
|
*/
|
||||||
int
|
bool
|
||||||
ParseVariableNum(const char *val,
|
ParseVariableNum(const char *value, const char *name, int *result)
|
||||||
int defaultval,
|
|
||||||
int faultval,
|
|
||||||
bool allowtrail)
|
|
||||||
{
|
{
|
||||||
int result;
|
char *end;
|
||||||
|
long numval;
|
||||||
|
|
||||||
if (!val)
|
if (value == NULL)
|
||||||
result = defaultval;
|
return false;
|
||||||
else if (!val[0])
|
errno = 0;
|
||||||
result = faultval;
|
numval = strtol(value, &end, 0);
|
||||||
|
if (errno == 0 && *end == '\0' && end != value && numval == (int) numval)
|
||||||
|
{
|
||||||
|
*result = (int) numval;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
char *end;
|
/* string is not recognized; don't clobber *result */
|
||||||
|
if (name)
|
||||||
result = strtol(val, &end, 0);
|
psql_error("invalid value \"%s\" for \"%s\": integer expected\n",
|
||||||
if (!allowtrail && *end)
|
value, name);
|
||||||
result = faultval;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 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
|
int
|
||||||
GetVariableNum(VariableSpace space,
|
GetVariableNum(VariableSpace space,
|
||||||
const char *name,
|
const char *name,
|
||||||
int defaultval,
|
int defaultval,
|
||||||
int faultval,
|
int faultval)
|
||||||
bool allowtrail)
|
|
||||||
{
|
{
|
||||||
const char *val;
|
const char *val;
|
||||||
|
int result;
|
||||||
|
|
||||||
val = GetVariable(space, name);
|
val = GetVariable(space, name);
|
||||||
return ParseVariableNum(val, defaultval, faultval, allowtrail);
|
if (!val)
|
||||||
|
return defaultval;
|
||||||
|
|
||||||
|
if (ParseVariableNum(val, NULL, &result))
|
||||||
|
return result;
|
||||||
|
else
|
||||||
|
return faultval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Print values of all variables.
|
||||||
|
*/
|
||||||
void
|
void
|
||||||
PrintVariables(VariableSpace space)
|
PrintVariables(VariableSpace space)
|
||||||
{
|
{
|
||||||
@ -184,17 +216,28 @@ PrintVariables(VariableSpace space)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set the variable named "name" to value "value",
|
||||||
|
* or delete it if "value" is NULL.
|
||||||
|
*
|
||||||
|
* Returns true if successful, false if not; in the latter case a suitable
|
||||||
|
* error message has been printed, except for the unexpected case of
|
||||||
|
* space or name being NULL.
|
||||||
|
*/
|
||||||
bool
|
bool
|
||||||
SetVariable(VariableSpace space, const char *name, const char *value)
|
SetVariable(VariableSpace space, const char *name, const char *value)
|
||||||
{
|
{
|
||||||
struct _variable *current,
|
struct _variable *current,
|
||||||
*previous;
|
*previous;
|
||||||
|
|
||||||
if (!space)
|
if (!space || !name)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!valid_variable_name(name))
|
if (!valid_variable_name(name))
|
||||||
|
{
|
||||||
|
psql_error("invalid variable name: \"%s\"\n", name);
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (!value)
|
if (!value)
|
||||||
return DeleteVariable(space, name);
|
return DeleteVariable(space, name);
|
||||||
@ -205,13 +248,30 @@ SetVariable(VariableSpace space, const char *name, const char *value)
|
|||||||
{
|
{
|
||||||
if (strcmp(current->name, name) == 0)
|
if (strcmp(current->name, name) == 0)
|
||||||
{
|
{
|
||||||
/* found entry, so update */
|
/*
|
||||||
if (current->value)
|
* Found entry, so update, unless hook returns false. The hook
|
||||||
free(current->value);
|
* may need the passed value to have the same lifespan as the
|
||||||
current->value = pg_strdup(value);
|
* variable, so allocate it right away, even though we'll have to
|
||||||
|
* free it again if the hook returns false.
|
||||||
|
*/
|
||||||
|
char *new_value = pg_strdup(value);
|
||||||
|
bool confirmed;
|
||||||
|
|
||||||
if (current->assign_hook)
|
if (current->assign_hook)
|
||||||
(*current->assign_hook) (current->value);
|
confirmed = (*current->assign_hook) (new_value);
|
||||||
return true;
|
else
|
||||||
|
confirmed = true;
|
||||||
|
|
||||||
|
if (confirmed)
|
||||||
|
{
|
||||||
|
if (current->value)
|
||||||
|
pg_free(current->value);
|
||||||
|
current->value = new_value;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
pg_free(new_value); /* current->value is left unchanged */
|
||||||
|
|
||||||
|
return confirmed;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -226,19 +286,29 @@ SetVariable(VariableSpace space, const char *name, const char *value)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This both sets a hook function, and calls it on the current value (if any)
|
* Attach an assign hook function to the named variable.
|
||||||
|
*
|
||||||
|
* If the variable doesn't already exist, create it with value NULL,
|
||||||
|
* just so we have a place to store the hook function. (Externally,
|
||||||
|
* this isn't different from it not being defined.)
|
||||||
|
*
|
||||||
|
* The hook is immediately called on the variable's current value. This is
|
||||||
|
* meant to let it update any derived psql state. If the hook doesn't like
|
||||||
|
* the current value, it will print a message to that effect, but we'll ignore
|
||||||
|
* it. Generally we do not expect any such failure here, because this should
|
||||||
|
* get called before any user-supplied value is assigned.
|
||||||
*/
|
*/
|
||||||
bool
|
void
|
||||||
SetVariableAssignHook(VariableSpace space, const char *name, VariableAssignHook hook)
|
SetVariableAssignHook(VariableSpace space, const char *name, VariableAssignHook hook)
|
||||||
{
|
{
|
||||||
struct _variable *current,
|
struct _variable *current,
|
||||||
*previous;
|
*previous;
|
||||||
|
|
||||||
if (!space)
|
if (!space || !name)
|
||||||
return false;
|
return;
|
||||||
|
|
||||||
if (!valid_variable_name(name))
|
if (!valid_variable_name(name))
|
||||||
return false;
|
return;
|
||||||
|
|
||||||
for (previous = space, current = space->next;
|
for (previous = space, current = space->next;
|
||||||
current;
|
current;
|
||||||
@ -248,8 +318,8 @@ SetVariableAssignHook(VariableSpace space, const char *name, VariableAssignHook
|
|||||||
{
|
{
|
||||||
/* found entry, so update */
|
/* found entry, so update */
|
||||||
current->assign_hook = hook;
|
current->assign_hook = hook;
|
||||||
(*hook) (current->value);
|
(void) (*hook) (current->value);
|
||||||
return true;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -260,16 +330,24 @@ SetVariableAssignHook(VariableSpace space, const char *name, VariableAssignHook
|
|||||||
current->assign_hook = hook;
|
current->assign_hook = hook;
|
||||||
current->next = NULL;
|
current->next = NULL;
|
||||||
previous->next = current;
|
previous->next = current;
|
||||||
(*hook) (NULL);
|
(void) (*hook) (NULL);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Convenience function to set a variable's value to "on".
|
||||||
|
*/
|
||||||
bool
|
bool
|
||||||
SetVariableBool(VariableSpace space, const char *name)
|
SetVariableBool(VariableSpace space, const char *name)
|
||||||
{
|
{
|
||||||
return SetVariable(space, name, "on");
|
return SetVariable(space, name, "on");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Attempt to delete variable.
|
||||||
|
*
|
||||||
|
* If unsuccessful, print a message and return "false".
|
||||||
|
* Deleting a nonexistent variable is not an error.
|
||||||
|
*/
|
||||||
bool
|
bool
|
||||||
DeleteVariable(VariableSpace space, const char *name)
|
DeleteVariable(VariableSpace space, const char *name)
|
||||||
{
|
{
|
||||||
@ -277,7 +355,7 @@ DeleteVariable(VariableSpace space, const char *name)
|
|||||||
*previous;
|
*previous;
|
||||||
|
|
||||||
if (!space)
|
if (!space)
|
||||||
return false;
|
return true;
|
||||||
|
|
||||||
for (previous = space, current = space->next;
|
for (previous = space, current = space->next;
|
||||||
current;
|
current;
|
||||||
@ -285,14 +363,21 @@ DeleteVariable(VariableSpace space, const char *name)
|
|||||||
{
|
{
|
||||||
if (strcmp(current->name, name) == 0)
|
if (strcmp(current->name, name) == 0)
|
||||||
{
|
{
|
||||||
if (current->value)
|
|
||||||
free(current->value);
|
|
||||||
current->value = NULL;
|
|
||||||
/* Physically delete only if no hook function to remember */
|
|
||||||
if (current->assign_hook)
|
if (current->assign_hook)
|
||||||
(*current->assign_hook) (NULL);
|
{
|
||||||
|
/* Allow deletion only if hook is okay with NULL value */
|
||||||
|
if (!(*current->assign_hook) (NULL))
|
||||||
|
return false; /* message printed by hook */
|
||||||
|
if (current->value)
|
||||||
|
free(current->value);
|
||||||
|
current->value = NULL;
|
||||||
|
/* Don't delete entry, or we'd forget the hook function */
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
/* We can delete the entry as well as its value */
|
||||||
|
if (current->value)
|
||||||
|
free(current->value);
|
||||||
previous->next = current->next;
|
previous->next = current->next;
|
||||||
free(current->name);
|
free(current->name);
|
||||||
free(current);
|
free(current);
|
||||||
@ -303,3 +388,16 @@ DeleteVariable(VariableSpace space, const char *name)
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Emit error with suggestions for variables or commands
|
||||||
|
* accepting enum-style arguments.
|
||||||
|
* This function just exists to standardize the wording.
|
||||||
|
* suggestions should follow the format "fee, fi, fo, fum".
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
PsqlVarEnumError(const char *name, const char *value, const char *suggestions)
|
||||||
|
{
|
||||||
|
psql_error("unrecognized value \"%s\" for \"%s\"\nAvailable values are: %s.\n",
|
||||||
|
value, name, suggestions);
|
||||||
|
}
|
||||||
|
@ -3,25 +3,39 @@
|
|||||||
*
|
*
|
||||||
* Copyright (c) 2000-2017, PostgreSQL Global Development Group
|
* Copyright (c) 2000-2017, PostgreSQL Global Development Group
|
||||||
*
|
*
|
||||||
|
* This implements a sort of variable repository. One could also think of it
|
||||||
|
* as a cheap version of an associative array. Each variable has a string
|
||||||
|
* name and a string value. The value can't be NULL, or more precisely
|
||||||
|
* that's not distinguishable from the variable being unset.
|
||||||
|
*
|
||||||
* src/bin/psql/variables.h
|
* src/bin/psql/variables.h
|
||||||
*/
|
*/
|
||||||
#ifndef VARIABLES_H
|
#ifndef VARIABLES_H
|
||||||
#define VARIABLES_H
|
#define VARIABLES_H
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This implements a sort of variable repository. One could also think of it
|
* Variables can be given "assign hook" functions. The assign hook can
|
||||||
* as a cheap version of an associative array. In each one of these
|
* prevent invalid values from being assigned, and can update internal C
|
||||||
* datastructures you can store name/value pairs. There can also be an
|
* variables to keep them in sync with the variable's current value.
|
||||||
* "assign hook" function that is called whenever the variable's value is
|
|
||||||
* changed.
|
|
||||||
*
|
*
|
||||||
* An "unset" operation causes the hook to be called with newval == NULL.
|
* A hook function is called before any attempted assignment, with the
|
||||||
|
* proposed new value of the variable (or with NULL, if an \unset is being
|
||||||
|
* attempted). If it returns false, the assignment doesn't occur --- it
|
||||||
|
* should print an error message with psql_error() to tell the user why.
|
||||||
|
*
|
||||||
|
* When a hook function is installed with SetVariableAssignHook(), it is
|
||||||
|
* called with the variable's current value (or with NULL, if it wasn't set
|
||||||
|
* yet). But its return value is ignored in this case. The hook should be
|
||||||
|
* set before any possibly-invalid value can be assigned.
|
||||||
|
*/
|
||||||
|
typedef bool (*VariableAssignHook) (const char *newval);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Data structure representing one variable.
|
||||||
*
|
*
|
||||||
* Note: if value == NULL then the variable is logically unset, but we are
|
* Note: if value == NULL then the variable is logically unset, but we are
|
||||||
* keeping the struct around so as not to forget about its hook function.
|
* keeping the struct around so as not to forget about its hook function.
|
||||||
*/
|
*/
|
||||||
typedef void (*VariableAssignHook) (const char *newval);
|
|
||||||
|
|
||||||
struct _variable
|
struct _variable
|
||||||
{
|
{
|
||||||
char *name;
|
char *name;
|
||||||
@ -30,27 +44,31 @@ struct _variable
|
|||||||
struct _variable *next;
|
struct _variable *next;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* Data structure representing a set of variables */
|
||||||
typedef struct _variable *VariableSpace;
|
typedef struct _variable *VariableSpace;
|
||||||
|
|
||||||
|
|
||||||
VariableSpace CreateVariableSpace(void);
|
VariableSpace CreateVariableSpace(void);
|
||||||
const char *GetVariable(VariableSpace space, const char *name);
|
const char *GetVariable(VariableSpace space, const char *name);
|
||||||
|
|
||||||
bool ParseVariableBool(const char *value, const char *name);
|
bool ParseVariableBool(const char *value, const char *name,
|
||||||
int ParseVariableNum(const char *val,
|
bool *result);
|
||||||
int defaultval,
|
|
||||||
int faultval,
|
bool ParseVariableNum(const char *value, const char *name,
|
||||||
bool allowtrail);
|
int *result);
|
||||||
|
|
||||||
int GetVariableNum(VariableSpace space,
|
int GetVariableNum(VariableSpace space,
|
||||||
const char *name,
|
const char *name,
|
||||||
int defaultval,
|
int defaultval,
|
||||||
int faultval,
|
int faultval);
|
||||||
bool allowtrail);
|
|
||||||
|
|
||||||
void PrintVariables(VariableSpace space);
|
void PrintVariables(VariableSpace space);
|
||||||
|
|
||||||
bool SetVariable(VariableSpace space, const char *name, const char *value);
|
bool SetVariable(VariableSpace space, const char *name, const char *value);
|
||||||
bool SetVariableAssignHook(VariableSpace space, const char *name, VariableAssignHook hook);
|
void SetVariableAssignHook(VariableSpace space, const char *name, VariableAssignHook hook);
|
||||||
bool SetVariableBool(VariableSpace space, const char *name);
|
bool SetVariableBool(VariableSpace space, const char *name);
|
||||||
bool DeleteVariable(VariableSpace space, const char *name);
|
bool DeleteVariable(VariableSpace space, const char *name);
|
||||||
|
|
||||||
|
void PsqlVarEnumError(const char *name, const char *value, const char *suggestions);
|
||||||
|
|
||||||
#endif /* VARIABLES_H */
|
#endif /* VARIABLES_H */
|
||||||
|
@ -2,6 +2,15 @@
|
|||||||
-- Tests for psql features that aren't closely connected to any
|
-- Tests for psql features that aren't closely connected to any
|
||||||
-- specific server features
|
-- specific server features
|
||||||
--
|
--
|
||||||
|
-- \set
|
||||||
|
-- fail: invalid name
|
||||||
|
\set invalid/name foo
|
||||||
|
invalid variable name: "invalid/name"
|
||||||
|
-- fail: invalid value for special variable
|
||||||
|
\set AUTOCOMMIT foo
|
||||||
|
unrecognized value "foo" for "AUTOCOMMIT": boolean expected
|
||||||
|
\set FETCH_COUNT foo
|
||||||
|
invalid value "foo" for "FETCH_COUNT": integer expected
|
||||||
-- \gset
|
-- \gset
|
||||||
select 10 as test01, 20 as test02, 'Hello' as test03 \gset pref01_
|
select 10 as test01, 20 as test02, 'Hello' as test03 \gset pref01_
|
||||||
\echo :pref01_test01 :pref01_test02 :pref01_test03
|
\echo :pref01_test01 :pref01_test02 :pref01_test03
|
||||||
@ -9,7 +18,7 @@ select 10 as test01, 20 as test02, 'Hello' as test03 \gset pref01_
|
|||||||
-- should fail: bad variable name
|
-- should fail: bad variable name
|
||||||
select 10 as "bad name"
|
select 10 as "bad name"
|
||||||
\gset
|
\gset
|
||||||
could not set variable "bad name"
|
invalid variable name: "bad name"
|
||||||
-- multiple backslash commands in one line
|
-- multiple backslash commands in one line
|
||||||
select 1 as x, 2 as y \gset pref01_ \\ \echo :pref01_x
|
select 1 as x, 2 as y \gset pref01_ \\ \echo :pref01_x
|
||||||
1
|
1
|
||||||
|
@ -3,6 +3,14 @@
|
|||||||
-- specific server features
|
-- specific server features
|
||||||
--
|
--
|
||||||
|
|
||||||
|
-- \set
|
||||||
|
|
||||||
|
-- fail: invalid name
|
||||||
|
\set invalid/name foo
|
||||||
|
-- fail: invalid value for special variable
|
||||||
|
\set AUTOCOMMIT foo
|
||||||
|
\set FETCH_COUNT foo
|
||||||
|
|
||||||
-- \gset
|
-- \gset
|
||||||
|
|
||||||
select 10 as test01, 20 as test02, 'Hello' as test03 \gset pref01_
|
select 10 as test01, 20 as test02, 'Hello' as test03 \gset pref01_
|
||||||
|
Loading…
x
Reference in New Issue
Block a user