Support non-ASCII letters in psql variable names.
As in the backend, the implementation actually accepts any non-ASCII character, but we only document that you can use letters.
This commit is contained in:
parent
910725b49d
commit
e86fdb0ab2
@ -2206,7 +2206,7 @@ lo_import 152801
|
|||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
Valid variable names can contain characters, digits, and
|
Valid variable names can contain letters, digits, and
|
||||||
underscores. See the section <xref
|
underscores. See the section <xref
|
||||||
linkend="APP-PSQL-variables"
|
linkend="APP-PSQL-variables"
|
||||||
endterm="APP-PSQL-variables-title"> below for details.
|
endterm="APP-PSQL-variables-title"> below for details.
|
||||||
@ -2461,8 +2461,12 @@ lo_import 152801
|
|||||||
<application>psql</application> provides variable substitution
|
<application>psql</application> provides variable substitution
|
||||||
features similar to common Unix command shells.
|
features similar to common Unix command shells.
|
||||||
Variables are simply name/value pairs, where the value
|
Variables are simply name/value pairs, where the value
|
||||||
can be any string of any length. To set variables, use the
|
can be any string of any length. The name must consist of letters
|
||||||
<application>psql</application> meta-command
|
(including non-Latin letters), digits, and underscores.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
To set a variable, use the <application>psql</application> meta-command
|
||||||
<command>\set</command>:
|
<command>\set</command>:
|
||||||
<programlisting>
|
<programlisting>
|
||||||
testdb=> <userinput>\set foo bar</userinput>
|
testdb=> <userinput>\set foo bar</userinput>
|
||||||
@ -2498,16 +2502,15 @@ bar
|
|||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
<application>psql</application>'s internal variable names can
|
A number of these variables are treated specially
|
||||||
consist of letters, numbers, and underscores in any order and any
|
by <application>psql</application>. They represent certain option
|
||||||
number of them. A number of these variables are treated specially
|
|
||||||
by <application>psql</application>. They indicate 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 that represent some state of the application. Although
|
the variable, or in some cases represent changeable state of
|
||||||
you can use these variables for any other purpose, this is not
|
<application>psql</application>. Although
|
||||||
|
you can use these variables for other purposes, this is not
|
||||||
recommended, as the program behavior might grow really strange
|
recommended, as the program behavior might grow really strange
|
||||||
really quickly. By convention, all specially treated variables
|
really quickly. By convention, all specially treated variables' names
|
||||||
consist of all upper-case letters (and possibly numbers 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
|
||||||
treated variables follows.
|
treated variables follows.
|
||||||
|
@ -995,7 +995,7 @@ exec_command(const char *cmd,
|
|||||||
|
|
||||||
if (!SetVariable(pset.vars, opt, result))
|
if (!SetVariable(pset.vars, opt, result))
|
||||||
{
|
{
|
||||||
psql_error("\\%s: error\n", cmd);
|
psql_error("\\%s: error while setting variable\n", cmd);
|
||||||
success = false;
|
success = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1096,7 +1096,7 @@ exec_command(const char *cmd,
|
|||||||
|
|
||||||
if (!SetVariable(pset.vars, opt0, newval))
|
if (!SetVariable(pset.vars, opt0, newval))
|
||||||
{
|
{
|
||||||
psql_error("\\%s: error\n", cmd);
|
psql_error("\\%s: error while setting variable\n", cmd);
|
||||||
success = false;
|
success = false;
|
||||||
}
|
}
|
||||||
free(newval);
|
free(newval);
|
||||||
@ -1272,7 +1272,7 @@ exec_command(const char *cmd,
|
|||||||
}
|
}
|
||||||
else if (!SetVariable(pset.vars, opt, NULL))
|
else if (!SetVariable(pset.vars, opt, NULL))
|
||||||
{
|
{
|
||||||
psql_error("\\%s: error\n", cmd);
|
psql_error("\\%s: error while setting variable\n", cmd);
|
||||||
success = false;
|
success = false;
|
||||||
}
|
}
|
||||||
free(opt);
|
free(opt);
|
||||||
|
@ -120,6 +120,7 @@ static bool var_is_current_source(PsqlScanState state, const char *varname);
|
|||||||
static YY_BUFFER_STATE prepare_buffer(const char *txt, int len,
|
static YY_BUFFER_STATE prepare_buffer(const char *txt, int len,
|
||||||
char **txtcopy);
|
char **txtcopy);
|
||||||
static void emit(const char *txt, int len);
|
static void emit(const char *txt, int len);
|
||||||
|
static char *extract_substring(const char *txt, int len);
|
||||||
static void escape_variable(bool as_ident);
|
static void escape_variable(bool as_ident);
|
||||||
|
|
||||||
#define ECHO emit(yytext, yyleng)
|
#define ECHO emit(yytext, yyleng)
|
||||||
@ -384,6 +385,9 @@ realfail2 ({integer}|{decimal})[Ee][-+]
|
|||||||
|
|
||||||
param \${integer}
|
param \${integer}
|
||||||
|
|
||||||
|
/* psql-specific: characters allowed in variable names */
|
||||||
|
variable_char [A-Za-z\200-\377_0-9]
|
||||||
|
|
||||||
other .
|
other .
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -680,11 +684,12 @@ other .
|
|||||||
return LEXRES_BACKSLASH;
|
return LEXRES_BACKSLASH;
|
||||||
}
|
}
|
||||||
|
|
||||||
:[A-Za-z0-9_]+ {
|
:{variable_char}+ {
|
||||||
/* Possible psql variable substitution */
|
/* Possible psql variable substitution */
|
||||||
const char *varname = yytext + 1;
|
char *varname;
|
||||||
const char *value;
|
const char *value;
|
||||||
|
|
||||||
|
varname = extract_substring(yytext + 1, yyleng - 1);
|
||||||
value = GetVariable(pset.vars, varname);
|
value = GetVariable(pset.vars, varname);
|
||||||
|
|
||||||
if (value)
|
if (value)
|
||||||
@ -713,13 +718,15 @@ other .
|
|||||||
*/
|
*/
|
||||||
ECHO;
|
ECHO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
free(varname);
|
||||||
}
|
}
|
||||||
|
|
||||||
:'[A-Za-z0-9_]+' {
|
:'{variable_char}+' {
|
||||||
escape_variable(false);
|
escape_variable(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
:\"[A-Za-z0-9_]+\" {
|
:\"{variable_char}+\" {
|
||||||
escape_variable(true);
|
escape_variable(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -728,13 +735,13 @@ other .
|
|||||||
* two rules above fails to match completely.
|
* two rules above fails to match completely.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
:'[A-Za-z0-9_]* {
|
:'{variable_char}* {
|
||||||
/* Throw back everything but the colon */
|
/* Throw back everything but the colon */
|
||||||
yyless(1);
|
yyless(1);
|
||||||
ECHO;
|
ECHO;
|
||||||
}
|
}
|
||||||
|
|
||||||
:\"[A-Za-z0-9_]* {
|
:\"{variable_char}* {
|
||||||
/* Throw back everything but the colon */
|
/* Throw back everything but the colon */
|
||||||
yyless(1);
|
yyless(1);
|
||||||
ECHO;
|
ECHO;
|
||||||
@ -930,15 +937,18 @@ other .
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
:[A-Za-z0-9_]+ {
|
:{variable_char}+ {
|
||||||
/* Possible psql variable substitution */
|
/* Possible psql variable substitution */
|
||||||
if (option_type == OT_VERBATIM)
|
if (option_type == OT_VERBATIM)
|
||||||
ECHO;
|
ECHO;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
char *varname;
|
||||||
const char *value;
|
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
|
* The variable value is just emitted without any
|
||||||
@ -956,7 +966,7 @@ other .
|
|||||||
return LEXRES_OK;
|
return LEXRES_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
:'[A-Za-z0-9_]+' {
|
:'{variable_char}+' {
|
||||||
if (option_type == OT_VERBATIM)
|
if (option_type == OT_VERBATIM)
|
||||||
ECHO;
|
ECHO;
|
||||||
else
|
else
|
||||||
@ -967,7 +977,7 @@ other .
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
:\"[A-Za-z0-9_]+\" {
|
:\"{variable_char}+\" {
|
||||||
if (option_type == OT_VERBATIM)
|
if (option_type == OT_VERBATIM)
|
||||||
ECHO;
|
ECHO;
|
||||||
else
|
else
|
||||||
@ -977,14 +987,14 @@ other .
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
:'[A-Za-z0-9_]* {
|
:'{variable_char}* {
|
||||||
/* Throw back everything but the colon */
|
/* Throw back everything but the colon */
|
||||||
yyless(1);
|
yyless(1);
|
||||||
ECHO;
|
ECHO;
|
||||||
BEGIN(xslashdefaultarg);
|
BEGIN(xslashdefaultarg);
|
||||||
}
|
}
|
||||||
|
|
||||||
:\"[A-Za-z0-9_]* {
|
:\"{variable_char}* {
|
||||||
/* Throw back everything but the colon */
|
/* Throw back everything but the colon */
|
||||||
yyless(1);
|
yyless(1);
|
||||||
ECHO;
|
ECHO;
|
||||||
@ -1844,16 +1854,58 @@ emit(const char *txt, int len)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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
|
static void
|
||||||
escape_variable(bool as_ident)
|
escape_variable(bool as_ident)
|
||||||
{
|
{
|
||||||
char saved_char;
|
char *varname;
|
||||||
const char *value;
|
const char *value;
|
||||||
|
|
||||||
/* Variable lookup. */
|
/* Variable lookup. */
|
||||||
saved_char = yytext[yyleng - 1];
|
varname = extract_substring(yytext + 2, yyleng - 3);
|
||||||
yytext[yyleng - 1] = '\0';
|
value = GetVariable(pset.vars, varname);
|
||||||
value = GetVariable(pset.vars, yytext + 2);
|
free(varname);
|
||||||
|
|
||||||
/* Escaping. */
|
/* Escaping. */
|
||||||
if (value)
|
if (value)
|
||||||
@ -1870,9 +1922,11 @@ escape_variable(bool as_ident)
|
|||||||
else
|
else
|
||||||
escaped_value =
|
escaped_value =
|
||||||
PQescapeLiteral(pset.db, value, strlen(value));
|
PQescapeLiteral(pset.db, value, strlen(value));
|
||||||
|
|
||||||
if (escaped_value == NULL)
|
if (escaped_value == NULL)
|
||||||
{
|
{
|
||||||
const char *error = PQerrorMessage(pset.db);
|
const char *error = PQerrorMessage(pset.db);
|
||||||
|
|
||||||
psql_error("%s", error);
|
psql_error("%s", error);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -1888,6 +1942,5 @@ escape_variable(bool as_ident)
|
|||||||
* If we reach this point, some kind of error has occurred. Emit the
|
* If we reach this point, some kind of error has occurred. Emit the
|
||||||
* original text into the output buffer.
|
* original text into the output buffer.
|
||||||
*/
|
*/
|
||||||
yytext[yyleng - 1] = saved_char;
|
|
||||||
emit(yytext, yyleng);
|
emit(yytext, yyleng);
|
||||||
}
|
}
|
||||||
|
@ -6,10 +6,40 @@
|
|||||||
* src/bin/psql/variables.c
|
* src/bin/psql/variables.c
|
||||||
*/
|
*/
|
||||||
#include "postgres_fe.h"
|
#include "postgres_fe.h"
|
||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "variables.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
|
* A "variable space" is represented by an otherwise-unused struct _variable
|
||||||
* that serves as list header.
|
* that serves as list header.
|
||||||
@ -158,7 +188,7 @@ SetVariable(VariableSpace space, const char *name, const char *value)
|
|||||||
if (!space)
|
if (!space)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (strspn(name, VALID_VARIABLE_CHARS) != strlen(name))
|
if (!valid_variable_name(name))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!value)
|
if (!value)
|
||||||
@ -202,7 +232,7 @@ SetVariableAssignHook(VariableSpace space, const char *name, VariableAssignHook
|
|||||||
if (!space)
|
if (!space)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (strspn(name, VALID_VARIABLE_CHARS) != strlen(name))
|
if (!valid_variable_name(name))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
for (previous = space, current = space->next;
|
for (previous = space, current = space->next;
|
||||||
|
@ -32,10 +32,6 @@ struct _variable
|
|||||||
|
|
||||||
typedef struct _variable *VariableSpace;
|
typedef struct _variable *VariableSpace;
|
||||||
|
|
||||||
/* Allowed chars in a variable's name */
|
|
||||||
#define VALID_VARIABLE_CHARS "abcdefghijklmnopqrstuvwxyz"\
|
|
||||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ" "0123456789_"
|
|
||||||
|
|
||||||
VariableSpace CreateVariableSpace(void);
|
VariableSpace CreateVariableSpace(void);
|
||||||
const char *GetVariable(VariableSpace space, const char *name);
|
const char *GetVariable(VariableSpace space, const char *name);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user