mirror of
https://git.postgresql.org/git/postgresql.git
synced 2024-12-21 08:29:39 +08:00
Fix psql to not go into infinite recursion when expanding a variable that
refers to itself (directly or indirectly). Instead, print a message when recursion is detected, and don't expand the repeated reference. Per bug #5448 from Francis Markham. Back-patch to 8.0. Although the issue exists in 7.4 as well, it seems impractical to fix there because of the lack of any state stack that could be used to track active expansions.
This commit is contained in:
parent
1ba23f767b
commit
93dc6a1b39
@ -33,7 +33,7 @@
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/bin/psql/psqlscan.l,v 1.32 2010/01/29 17:44:12 rhaas Exp $
|
||||
* $PostgreSQL: pgsql/src/bin/psql/psqlscan.l,v 1.33 2010/05/05 22:18:56 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -59,6 +59,7 @@ typedef struct StackElem
|
||||
YY_BUFFER_STATE buf; /* flex input control structure */
|
||||
char *bufstring; /* data actually being scanned by flex */
|
||||
char *origstring; /* copy of original data, if needed */
|
||||
char *varname; /* name of variable providing data, or NULL */
|
||||
struct StackElem *next;
|
||||
} StackElem;
|
||||
|
||||
@ -113,7 +114,9 @@ static char *option_quote;
|
||||
|
||||
int yylex(void);
|
||||
|
||||
static void push_new_buffer(const char *newstr);
|
||||
static void push_new_buffer(const char *newstr, const char *varname);
|
||||
static void pop_buffer_stack(PsqlScanState state);
|
||||
static bool var_is_current_source(PsqlScanState state, const char *varname);
|
||||
static YY_BUFFER_STATE prepare_buffer(const char *txt, int len,
|
||||
char **txtcopy);
|
||||
static void emit(const char *txt, int len);
|
||||
@ -688,15 +691,28 @@ other .
|
||||
|
||||
:[A-Za-z0-9_]+ {
|
||||
/* Possible psql variable substitution */
|
||||
const char *varname = yytext + 1;
|
||||
const char *value;
|
||||
|
||||
value = GetVariable(pset.vars, yytext + 1);
|
||||
value = GetVariable(pset.vars, varname);
|
||||
|
||||
if (value)
|
||||
{
|
||||
/* It is a variable, perform substitution */
|
||||
push_new_buffer(value);
|
||||
/* yy_scan_string already made buffer active */
|
||||
/* It is a variable, check for recursion */
|
||||
if (var_is_current_source(cur_state, varname))
|
||||
{
|
||||
/* Recursive expansion --- don't go there */
|
||||
psql_error("skipping recursive expansion of variable \"%s\"\n",
|
||||
varname);
|
||||
/* Instead copy the string as is */
|
||||
ECHO;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* OK, perform substitution */
|
||||
push_new_buffer(value, varname);
|
||||
/* yy_scan_string already made buffer active */
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -836,12 +852,7 @@ other .
|
||||
* We were expanding a variable, so pop the inclusion
|
||||
* stack and keep lexing
|
||||
*/
|
||||
cur_state->buffer_stack = stackelem->next;
|
||||
yy_delete_buffer(stackelem->buf);
|
||||
free(stackelem->bufstring);
|
||||
if (stackelem->origstring)
|
||||
free(stackelem->origstring);
|
||||
free(stackelem);
|
||||
pop_buffer_stack(cur_state);
|
||||
|
||||
stackelem = cur_state->buffer_stack;
|
||||
if (stackelem != NULL)
|
||||
@ -926,6 +937,7 @@ other .
|
||||
* further examination. This is consistent with the
|
||||
* pre-8.0 code behavior, if not with the way that
|
||||
* variables are handled outside backslash commands.
|
||||
* Note that we needn't guard against recursion here.
|
||||
*/
|
||||
if (value)
|
||||
appendPQExpBufferStr(output_buf, value);
|
||||
@ -1315,16 +1327,7 @@ psql_scan_finish(PsqlScanState state)
|
||||
{
|
||||
/* Drop any incomplete variable expansions. */
|
||||
while (state->buffer_stack != NULL)
|
||||
{
|
||||
StackElem *stackelem = state->buffer_stack;
|
||||
|
||||
state->buffer_stack = stackelem->next;
|
||||
yy_delete_buffer(stackelem->buf);
|
||||
free(stackelem->bufstring);
|
||||
if (stackelem->origstring)
|
||||
free(stackelem->origstring);
|
||||
free(stackelem);
|
||||
}
|
||||
pop_buffer_stack(state);
|
||||
|
||||
/* Done with the outer scan buffer, too */
|
||||
if (state->scanbufhandle)
|
||||
@ -1670,11 +1673,19 @@ psql_scan_slash_command_end(PsqlScanState state)
|
||||
* NOTE SIDE EFFECT: the new buffer is made the active flex input buffer.
|
||||
*/
|
||||
static void
|
||||
push_new_buffer(const char *newstr)
|
||||
push_new_buffer(const char *newstr, const char *varname)
|
||||
{
|
||||
StackElem *stackelem;
|
||||
|
||||
stackelem = (StackElem *) pg_malloc(sizeof(StackElem));
|
||||
|
||||
/*
|
||||
* In current usage, the passed varname points at the current flex
|
||||
* input buffer; we must copy it before calling prepare_buffer()
|
||||
* because that will change the buffer state.
|
||||
*/
|
||||
stackelem->varname = varname ? pg_strdup(varname) : NULL;
|
||||
|
||||
stackelem->buf = prepare_buffer(newstr, strlen(newstr),
|
||||
&stackelem->bufstring);
|
||||
cur_state->curline = stackelem->bufstring;
|
||||
@ -1692,6 +1703,46 @@ push_new_buffer(const char *newstr)
|
||||
cur_state->buffer_stack = stackelem;
|
||||
}
|
||||
|
||||
/*
|
||||
* Pop the topmost buffer stack item (there must be one!)
|
||||
*
|
||||
* NB: after this, the flex input state is unspecified; caller must
|
||||
* switch to an appropriate buffer to continue lexing.
|
||||
*/
|
||||
static void
|
||||
pop_buffer_stack(PsqlScanState state)
|
||||
{
|
||||
StackElem *stackelem = state->buffer_stack;
|
||||
|
||||
state->buffer_stack = stackelem->next;
|
||||
yy_delete_buffer(stackelem->buf);
|
||||
free(stackelem->bufstring);
|
||||
if (stackelem->origstring)
|
||||
free(stackelem->origstring);
|
||||
if (stackelem->varname)
|
||||
free(stackelem->varname);
|
||||
free(stackelem);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if specified variable name is the source for any string
|
||||
* currently being scanned
|
||||
*/
|
||||
static bool
|
||||
var_is_current_source(PsqlScanState state, const char *varname)
|
||||
{
|
||||
StackElem *stackelem;
|
||||
|
||||
for (stackelem = state->buffer_stack;
|
||||
stackelem != NULL;
|
||||
stackelem = stackelem->next)
|
||||
{
|
||||
if (stackelem->varname && strcmp(stackelem->varname, varname) == 0)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set up a flex input buffer to scan the given data. We always make a
|
||||
* copy of the data. If working in an unsafe encoding, the copy has
|
||||
|
Loading…
Reference in New Issue
Block a user