</para>
<para>
- Valid variable names can contain characters, digits, and
+ Valid variable names can contain letters, digits, and
underscores. See the section <xref
linkend="APP-PSQL-variables"
endterm="APP-PSQL-variables-title"> below for details.
<application>psql</application> provides variable substitution
features similar to common Unix command shells.
Variables are simply name/value pairs, where the value
- can be any string of any length. To set variables, use the
- <application>psql</application> meta-command
+ can be any string of any length. The name must consist of letters
+ (including non-Latin letters), digits, and underscores.
+ </para>
+
+ <para>
+ To set a variable, use the <application>psql</application> meta-command
<command>\set</command>:
<programlisting>
testdb=> <userinput>\set foo bar</userinput>
</para>
<para>
- <application>psql</application>'s internal variable names can
- consist of letters, numbers, and underscores in any order and any
- number of them. A number of these variables are treated specially
- by <application>psql</application>. They indicate certain option
+ A number of these variables are treated specially
+ by <application>psql</application>. They represent certain option
settings that can be changed at run time by altering the value of
- the variable or that represent some state of the application. Although
- you can use these variables for any other purpose, this is not
+ the variable, or in some cases represent changeable state of
+ <application>psql</application>. Although
+ you can use these variables for other purposes, this is not
recommended, as the program behavior might grow really strange
- really quickly. By convention, all specially treated variables
- consist of all upper-case letters (and possibly numbers and
+ really quickly. By convention, all specially treated variables' names
+ consist of all upper-case ASCII letters (and possibly digits and
underscores). To ensure maximum compatibility in the future, avoid
using such variable names for your own purposes. A list of all specially
treated variables follows.
if (!SetVariable(pset.vars, opt, result))
{
- psql_error("\\%s: error\n", cmd);
+ psql_error("\\%s: error while setting variable\n", cmd);
success = false;
}
if (!SetVariable(pset.vars, opt0, newval))
{
- psql_error("\\%s: error\n", cmd);
+ psql_error("\\%s: error while setting variable\n", cmd);
success = false;
}
free(newval);
}
else if (!SetVariable(pset.vars, opt, NULL))
{
- psql_error("\\%s: error\n", cmd);
+ psql_error("\\%s: error while setting variable\n", cmd);
success = false;
}
free(opt);
static YY_BUFFER_STATE prepare_buffer(const char *txt, int len,
char **txtcopy);
static void emit(const char *txt, int len);
+static char *extract_substring(const char *txt, int len);
static void escape_variable(bool as_ident);
#define ECHO emit(yytext, yyleng)
param \${integer}
+/* psql-specific: characters allowed in variable names */
+variable_char [A-Za-z\200-\377_0-9]
+
other .
/*
return LEXRES_BACKSLASH;
}
-:[A-Za-z0-9_]+ {
+:{variable_char}+ {
/* Possible psql variable substitution */
- const char *varname = yytext + 1;
+ char *varname;
const char *value;
+ varname = extract_substring(yytext + 1, yyleng - 1);
value = GetVariable(pset.vars, varname);
if (value)
*/
ECHO;
}
+
+ free(varname);
}
-:'[A-Za-z0-9_]+' {
+:'{variable_char}+' {
escape_variable(false);
}
-:\"[A-Za-z0-9_]+\" {
+:\"{variable_char}+\" {
escape_variable(true);
}
* two rules above fails to match completely.
*/
-:'[A-Za-z0-9_]* {
+:'{variable_char}* {
/* Throw back everything but the colon */
yyless(1);
ECHO;
}
-:\"[A-Za-z0-9_]* {
+:\"{variable_char}* {
/* Throw back everything but the colon */
yyless(1);
ECHO;
}
}
-:[A-Za-z0-9_]+ {
+:{variable_char}+ {
/* Possible psql variable substitution */
if (option_type == OT_VERBATIM)
ECHO;
else
{
+ char *varname;
const char *value;
- value = GetVariable(pset.vars, yytext + 1);
+ varname = extract_substring(yytext + 1, yyleng - 1);
+ value = GetVariable(pset.vars, varname);
+ free(varname);
/*
* The variable value is just emitted without any
return LEXRES_OK;
}
-:'[A-Za-z0-9_]+' {
+:'{variable_char}+' {
if (option_type == OT_VERBATIM)
ECHO;
else
}
-:\"[A-Za-z0-9_]+\" {
+:\"{variable_char}+\" {
if (option_type == OT_VERBATIM)
ECHO;
else
}
}
-:'[A-Za-z0-9_]* {
+:'{variable_char}* {
/* Throw back everything but the colon */
yyless(1);
ECHO;
BEGIN(xslashdefaultarg);
}
-:\"[A-Za-z0-9_]* {
+:\"{variable_char}* {
/* Throw back everything but the colon */
yyless(1);
ECHO;
}
}
+/*
+ * extract_substring --- fetch the true value of (part of) the current token
+ *
+ * This is like emit(), except that the data is returned as a malloc'd string
+ * rather than being pushed directly to output_buf.
+ */
+static char *
+extract_substring(const char *txt, int len)
+{
+ char *result = (char *) pg_malloc(len + 1);
+
+ if (cur_state->safe_encoding)
+ memcpy(result, txt, len);
+ else
+ {
+ /* Gotta do it the hard way */
+ const char *reference = cur_state->refline;
+ int i;
+
+ reference += (txt - cur_state->curline);
+
+ for (i = 0; i < len; i++)
+ {
+ char ch = txt[i];
+
+ if (ch == (char) 0xFF)
+ ch = reference[i];
+ result[i] = ch;
+ }
+ }
+ result[len] = '\0';
+ return result;
+}
+
+/*
+ * escape_variable --- process :'VARIABLE' or :"VARIABLE"
+ *
+ * If the variable name is found, escape its value using the appropriate
+ * quoting method and emit the value to output_buf. (Since the result is
+ * surely quoted, there is never any reason to rescan it.) If we don't
+ * find the variable or the escaping function fails, emit the token as-is.
+ */
static void
escape_variable(bool as_ident)
{
- char saved_char;
+ char *varname;
const char *value;
/* Variable lookup. */
- saved_char = yytext[yyleng - 1];
- yytext[yyleng - 1] = '\0';
- value = GetVariable(pset.vars, yytext + 2);
+ varname = extract_substring(yytext + 2, yyleng - 3);
+ value = GetVariable(pset.vars, varname);
+ free(varname);
/* Escaping. */
if (value)
else
escaped_value =
PQescapeLiteral(pset.db, value, strlen(value));
+
if (escaped_value == NULL)
{
const char *error = PQerrorMessage(pset.db);
+
psql_error("%s", error);
}
else
* If we reach this point, some kind of error has occurred. Emit the
* original text into the output buffer.
*/
- yytext[yyleng - 1] = saved_char;
emit(yytext, yyleng);
}
* src/bin/psql/variables.c
*/
#include "postgres_fe.h"
+
#include "common.h"
#include "variables.h"
+/*
+ * Check whether a variable's name is allowed.
+ *
+ * We allow any non-ASCII character, as well as ASCII letters, digits, and
+ * underscore. Keep this in sync with the definition of variable_char in
+ * psqlscan.l.
+ */
+static bool
+valid_variable_name(const char *name)
+{
+ const unsigned char *ptr = (const unsigned char *) name;
+
+ /* Mustn't be zero-length */
+ if (*ptr == '\0')
+ return false;
+
+ while (*ptr)
+ {
+ if (IS_HIGHBIT_SET(*ptr) ||
+ strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz"
+ "_0123456789", *ptr) != NULL)
+ ptr++;
+ else
+ return false;
+ }
+
+ return true;
+}
+
/*
* A "variable space" is represented by an otherwise-unused struct _variable
* that serves as list header.
if (!space)
return false;
- if (strspn(name, VALID_VARIABLE_CHARS) != strlen(name))
+ if (!valid_variable_name(name))
return false;
if (!value)
if (!space)
return false;
- if (strspn(name, VALID_VARIABLE_CHARS) != strlen(name))
+ if (!valid_variable_name(name))
return false;
for (previous = space, current = space->next;
typedef struct _variable *VariableSpace;
-/* Allowed chars in a variable's name */
-#define VALID_VARIABLE_CHARS "abcdefghijklmnopqrstuvwxyz"\
- "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "0123456789_"
-
VariableSpace CreateVariableSpace(void);
const char *GetVariable(VariableSpace space, const char *name);