* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/bin/psql/psqlscan.l,v 1.20 2006/05/31 22:11:44 tgl Exp $
+ * $PostgreSQL: pgsql/src/bin/psql/psqlscan.l,v 1.20.2.1 2010/05/05 22:19:18 tgl Exp $
*
*-------------------------------------------------------------------------
*/
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;
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);
:[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
{
* 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)
* 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);
{
/* 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)
/* needed for push_new_buffer */
cur_state = state;
- push_new_buffer(str);
+ push_new_buffer(str, NULL);
}
* 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;
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