* Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/bin/psql/psqlscan.l,v 1.9 2004/12/31 22:03:15 pgsql Exp $
+ *       $PostgreSQL: pgsql/src/bin/psql/psqlscan.l,v 1.9.4.1 2010/05/05 22:19:31 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