*** pgsql/src/bin/psql/psqlscan.l 2005/06/26 19:16:06 1.15 --- pgsql/src/bin/psql/psqlscan.l 2010/05/05 22:19:24 1.15.2.1 *************** *** 33,39 **** * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION ! * $PostgreSQL: pgsql/src/bin/psql/psqlscan.l,v 1.14 2005/06/02 17:45:19 tgl Exp $ * *------------------------------------------------------------------------- */ --- 33,39 ---- * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION ! * $PostgreSQL: pgsql/src/bin/psql/psqlscan.l,v 1.15 2005/06/26 19:16:06 tgl Exp $ * *------------------------------------------------------------------------- */ *************** typedef struct StackElem *** 61,66 **** --- 61,67 ---- 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; *************** static char *option_quote; *** 115,121 **** int yylex(void); ! static void push_new_buffer(const char *newstr); static YY_BUFFER_STATE prepare_buffer(const char *txt, int len, char **txtcopy); static void emit(const char *txt, int len); --- 116,124 ---- int yylex(void); ! 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); *************** other . *** 585,599 **** :[A-Za-z0-9_]+ { /* Possible psql variable substitution */ const char *value; ! value = GetVariable(pset.vars, yytext + 1); if (value) { ! /* It is a variable, perform substitution */ ! push_new_buffer(value); ! /* yy_scan_string already made buffer active */ } else { --- 588,615 ---- :[A-Za-z0-9_]+ { /* Possible psql variable substitution */ + const char *varname = yytext + 1; const char *value; ! value = GetVariable(pset.vars, varname); if (value) { ! /* 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 { *************** other . *** 720,731 **** * 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); stackelem = cur_state->buffer_stack; if (stackelem != NULL) --- 736,742 ---- * We were expanding a variable, so pop the inclusion * stack and keep lexing */ ! pop_buffer_stack(cur_state); stackelem = cur_state->buffer_stack; if (stackelem != NULL) *************** other . *** 810,815 **** --- 821,827 ---- * 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); *************** psql_scan_finish(PsqlScanState state) *** 1169,1184 **** { /* 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); ! } /* Done with the outer scan buffer, too */ if (state->scanbufhandle) --- 1181,1187 ---- { /* Drop any incomplete variable expansions. */ while (state->buffer_stack != NULL) ! pop_buffer_stack(state); /* Done with the outer scan buffer, too */ if (state->scanbufhandle) *************** psql_scan_slash_pushback(PsqlScanState s *** 1522,1528 **** /* needed for push_new_buffer */ cur_state = state; ! push_new_buffer(str); } --- 1525,1531 ---- /* needed for push_new_buffer */ cur_state = state; ! push_new_buffer(str, NULL); } *************** psql_scan_slash_pushback(PsqlScanState s *** 1534,1544 **** * NOTE SIDE EFFECT: the new buffer is made the active flex input buffer. */ static void ! push_new_buffer(const char *newstr) { StackElem *stackelem; stackelem = (StackElem *) pg_malloc(sizeof(StackElem)); stackelem->buf = prepare_buffer(newstr, strlen(newstr), &stackelem->bufstring); cur_state->curline = stackelem->bufstring; --- 1537,1555 ---- * NOTE SIDE EFFECT: the new buffer is made the active flex input buffer. */ static void ! 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; *************** push_new_buffer(const char *newstr) *** 1557,1562 **** --- 1568,1613 ---- } /* + * 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 * multibyte sequences replaced by FFs to avoid fooling the lexer rules.