Commit 36481474 authored by Tom Lane's avatar Tom Lane

Get rid of "lookahead" functionality in plpgsql's yylex() function,

and instead make the grammar production for the RETURN statement do the
heavy lifting.  The lookahead idea was copied from the main parser, but
it does not work in plpgsql's parser because here gram.y looks explicitly
at the scanner's yytext variable, which will be out of sync after a
failed lookahead step.  A minimal example is

create or replace function foo() returns void language plpgsql as '
begin
  perform return foo bar;
end';

which can be seen by testing to deliver "foo foo bar" to the main parser
instead of the expected "return foo bar".  This isn't a huge bug since
RETURN is not found in the main grammar, but it could bite someone who
tried to use "return" as an identifier.

Back-patch to 8.1.  Bug exists further back, but HEAD patch doesn't apply
cleanly, and given the lack of field complaints it doesn't seem worth
the effort to develop adjusted patches.
parent 1b67e0cc
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.93 2006/06/16 23:29:26 tgl Exp $ * $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.94 2006/08/14 00:46:53 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -28,8 +28,10 @@ static PLpgSQL_expr *read_sql_construct(int until, ...@@ -28,8 +28,10 @@ static PLpgSQL_expr *read_sql_construct(int until,
int *endtoken); int *endtoken);
static PLpgSQL_expr *read_sql_stmt(const char *sqlstart); static PLpgSQL_expr *read_sql_stmt(const char *sqlstart);
static PLpgSQL_type *read_datatype(int tok); static PLpgSQL_type *read_datatype(int tok);
static PLpgSQL_stmt *make_select_stmt(void); static PLpgSQL_stmt *make_select_stmt(int lineno);
static PLpgSQL_stmt *make_fetch_stmt(void); static PLpgSQL_stmt *make_fetch_stmt(int lineno, int curvar);
static PLpgSQL_stmt *make_return_stmt(int lineno);
static PLpgSQL_stmt *make_return_next_stmt(int lineno);
static void check_assignable(PLpgSQL_datum *datum); static void check_assignable(PLpgSQL_datum *datum);
static PLpgSQL_row *read_into_scalar_list(const char *initial_name, static PLpgSQL_row *read_into_scalar_list(const char *initial_name,
PLpgSQL_datum *initial_datum); PLpgSQL_datum *initial_datum);
...@@ -118,7 +120,7 @@ static void check_labels(const char *start_label, ...@@ -118,7 +120,7 @@ static void check_labels(const char *start_label,
%type <loop_body> loop_body %type <loop_body> loop_body
%type <stmt> proc_stmt pl_block %type <stmt> proc_stmt pl_block
%type <stmt> stmt_assign stmt_if stmt_loop stmt_while stmt_exit %type <stmt> stmt_assign stmt_if stmt_loop stmt_while stmt_exit
%type <stmt> stmt_return stmt_return_next stmt_raise stmt_execsql %type <stmt> stmt_return stmt_raise stmt_execsql
%type <stmt> stmt_for stmt_select stmt_perform %type <stmt> stmt_for stmt_select stmt_perform
%type <stmt> stmt_dynexecute stmt_getdiag %type <stmt> stmt_dynexecute stmt_getdiag
%type <stmt> stmt_open stmt_fetch stmt_close stmt_null %type <stmt> stmt_open stmt_fetch stmt_close stmt_null
...@@ -183,7 +185,6 @@ static void check_labels(const char *start_label, ...@@ -183,7 +185,6 @@ static void check_labels(const char *start_label,
%token K_RENAME %token K_RENAME
%token K_RESULT_OID %token K_RESULT_OID
%token K_RETURN %token K_RETURN
%token K_RETURN_NEXT
%token K_REVERSE %token K_REVERSE
%token K_SELECT %token K_SELECT
%token K_STRICT %token K_STRICT
...@@ -596,8 +597,6 @@ proc_stmt : pl_block ';' ...@@ -596,8 +597,6 @@ proc_stmt : pl_block ';'
{ $$ = $1; } { $$ = $1; }
| stmt_return | stmt_return
{ $$ = $1; } { $$ = $1; }
| stmt_return_next
{ $$ = $1; }
| stmt_raise | stmt_raise
{ $$ = $1; } { $$ = $1; }
| stmt_execsql | stmt_execsql
...@@ -1130,8 +1129,7 @@ for_variable : T_SCALAR ...@@ -1130,8 +1129,7 @@ for_variable : T_SCALAR
stmt_select : K_SELECT lno stmt_select : K_SELECT lno
{ {
$$ = make_select_stmt(); $$ = make_select_stmt($2);
$$->lineno = $2;
} }
; ;
...@@ -1162,109 +1160,18 @@ exit_type : K_EXIT ...@@ -1162,109 +1160,18 @@ exit_type : K_EXIT
stmt_return : K_RETURN lno stmt_return : K_RETURN lno
{ {
PLpgSQL_stmt_return *new; int tok;
new = palloc0(sizeof(PLpgSQL_stmt_return));
new->cmd_type = PLPGSQL_STMT_RETURN;
new->lineno = $2;
new->expr = NULL;
new->retvarno = -1;
if (plpgsql_curr_compile->fn_retset) tok = yylex();
{ if (tok == K_NEXT)
if (yylex() != ';')
yyerror("RETURN cannot have a parameter in function returning set; use RETURN NEXT");
}
else if (plpgsql_curr_compile->out_param_varno >= 0)
{
if (yylex() != ';')
yyerror("RETURN cannot have a parameter in function with OUT parameters");
new->retvarno = plpgsql_curr_compile->out_param_varno;
}
else if (plpgsql_curr_compile->fn_rettype == VOIDOID)
{ {
if (yylex() != ';') $$ = make_return_next_stmt($2);
yyerror("RETURN cannot have a parameter in function returning void");
}
else if (plpgsql_curr_compile->fn_retistuple)
{
switch (yylex())
{
case K_NULL:
/* we allow this to support RETURN NULL in triggers */
break;
case T_ROW:
new->retvarno = yylval.row->rowno;
break;
case T_RECORD:
new->retvarno = yylval.rec->recno;
break;
default:
yyerror("RETURN must specify a record or row variable in function returning tuple");
break;
}
if (yylex() != ';')
yyerror("RETURN must specify a record or row variable in function returning tuple");
} }
else else
{ {
/* plpgsql_push_back_token(tok);
* Note that a well-formed expression is $$ = make_return_stmt($2);
* _required_ here; anything else is a
* compile-time error.
*/
new->expr = plpgsql_read_expression(';', ";");
} }
$$ = (PLpgSQL_stmt *)new;
}
;
stmt_return_next: K_RETURN_NEXT lno
{
PLpgSQL_stmt_return_next *new;
if (!plpgsql_curr_compile->fn_retset)
yyerror("cannot use RETURN NEXT in a non-SETOF function");
new = palloc0(sizeof(PLpgSQL_stmt_return_next));
new->cmd_type = PLPGSQL_STMT_RETURN_NEXT;
new->lineno = $2;
new->expr = NULL;
new->retvarno = -1;
if (plpgsql_curr_compile->out_param_varno >= 0)
{
if (yylex() != ';')
yyerror("RETURN NEXT cannot have a parameter in function with OUT parameters");
new->retvarno = plpgsql_curr_compile->out_param_varno;
}
else if (plpgsql_curr_compile->fn_retistuple)
{
switch (yylex())
{
case T_ROW:
new->retvarno = yylval.row->rowno;
break;
case T_RECORD:
new->retvarno = yylval.rec->recno;
break;
default:
yyerror("RETURN NEXT must specify a record or row variable in function returning tuple");
break;
}
if (yylex() != ';')
yyerror("RETURN NEXT must specify a record or row variable in function returning tuple");
}
else
new->expr = plpgsql_read_expression(';', ";");
$$ = (PLpgSQL_stmt *)new;
} }
; ;
...@@ -1537,13 +1444,7 @@ stmt_open : K_OPEN lno cursor_varptr ...@@ -1537,13 +1444,7 @@ stmt_open : K_OPEN lno cursor_varptr
stmt_fetch : K_FETCH lno cursor_variable K_INTO stmt_fetch : K_FETCH lno cursor_variable K_INTO
{ {
PLpgSQL_stmt_fetch *new; $$ = make_fetch_stmt($2, $3);
new = (PLpgSQL_stmt_fetch *)make_fetch_stmt();
new->curvar = $3;
$$ = (PLpgSQL_stmt *)new;
$$->lineno = $2;
} }
; ;
...@@ -1991,7 +1892,7 @@ read_datatype(int tok) ...@@ -1991,7 +1892,7 @@ read_datatype(int tok)
} }
static PLpgSQL_stmt * static PLpgSQL_stmt *
make_select_stmt(void) make_select_stmt(int lineno)
{ {
PLpgSQL_dstring ds; PLpgSQL_dstring ds;
int nparams = 0; int nparams = 0;
...@@ -2112,6 +2013,7 @@ make_select_stmt(void) ...@@ -2112,6 +2013,7 @@ make_select_stmt(void)
select = palloc0(sizeof(PLpgSQL_stmt_select)); select = palloc0(sizeof(PLpgSQL_stmt_select));
select->cmd_type = PLPGSQL_STMT_SELECT; select->cmd_type = PLPGSQL_STMT_SELECT;
select->lineno = lineno;
select->rec = rec; select->rec = rec;
select->row = row; select->row = row;
select->query = expr; select->query = expr;
...@@ -2125,6 +2027,7 @@ make_select_stmt(void) ...@@ -2125,6 +2027,7 @@ make_select_stmt(void)
execsql = palloc(sizeof(PLpgSQL_stmt_execsql)); execsql = palloc(sizeof(PLpgSQL_stmt_execsql));
execsql->cmd_type = PLPGSQL_STMT_EXECSQL; execsql->cmd_type = PLPGSQL_STMT_EXECSQL;
execsql->lineno = lineno;
execsql->sqlstmt = expr; execsql->sqlstmt = expr;
return (PLpgSQL_stmt *)execsql; return (PLpgSQL_stmt *)execsql;
...@@ -2133,7 +2036,7 @@ make_select_stmt(void) ...@@ -2133,7 +2036,7 @@ make_select_stmt(void)
static PLpgSQL_stmt * static PLpgSQL_stmt *
make_fetch_stmt(void) make_fetch_stmt(int lineno, int curvar)
{ {
int tok; int tok;
PLpgSQL_row *row = NULL; PLpgSQL_row *row = NULL;
...@@ -2172,12 +2075,124 @@ make_fetch_stmt(void) ...@@ -2172,12 +2075,124 @@ make_fetch_stmt(void)
if (tok != ';') if (tok != ';')
yyerror("syntax error"); yyerror("syntax error");
fetch = palloc0(sizeof(PLpgSQL_stmt_select)); fetch = palloc0(sizeof(PLpgSQL_stmt_fetch));
fetch->cmd_type = PLPGSQL_STMT_FETCH; fetch->cmd_type = PLPGSQL_STMT_FETCH;
fetch->rec = rec; fetch->lineno = lineno;
fetch->row = row; fetch->rec = rec;
fetch->row = row;
fetch->curvar = curvar;
return (PLpgSQL_stmt *) fetch;
}
static PLpgSQL_stmt *
make_return_stmt(int lineno)
{
PLpgSQL_stmt_return *new;
new = palloc0(sizeof(PLpgSQL_stmt_return));
new->cmd_type = PLPGSQL_STMT_RETURN;
new->lineno = lineno;
new->expr = NULL;
new->retvarno = -1;
if (plpgsql_curr_compile->fn_retset)
{
if (yylex() != ';')
yyerror("RETURN cannot have a parameter in function returning set; use RETURN NEXT");
}
else if (plpgsql_curr_compile->out_param_varno >= 0)
{
if (yylex() != ';')
yyerror("RETURN cannot have a parameter in function with OUT parameters");
new->retvarno = plpgsql_curr_compile->out_param_varno;
}
else if (plpgsql_curr_compile->fn_rettype == VOIDOID)
{
if (yylex() != ';')
yyerror("RETURN cannot have a parameter in function returning void");
}
else if (plpgsql_curr_compile->fn_retistuple)
{
switch (yylex())
{
case K_NULL:
/* we allow this to support RETURN NULL in triggers */
break;
case T_ROW:
new->retvarno = yylval.row->rowno;
break;
case T_RECORD:
new->retvarno = yylval.rec->recno;
break;
default:
yyerror("RETURN must specify a record or row variable in function returning tuple");
break;
}
if (yylex() != ';')
yyerror("RETURN must specify a record or row variable in function returning tuple");
}
else
{
/*
* Note that a well-formed expression is
* _required_ here; anything else is a
* compile-time error.
*/
new->expr = plpgsql_read_expression(';', ";");
}
return (PLpgSQL_stmt *) new;
}
static PLpgSQL_stmt *
make_return_next_stmt(int lineno)
{
PLpgSQL_stmt_return_next *new;
if (!plpgsql_curr_compile->fn_retset)
yyerror("cannot use RETURN NEXT in a non-SETOF function");
new = palloc0(sizeof(PLpgSQL_stmt_return_next));
new->cmd_type = PLPGSQL_STMT_RETURN_NEXT;
new->lineno = lineno;
new->expr = NULL;
new->retvarno = -1;
if (plpgsql_curr_compile->out_param_varno >= 0)
{
if (yylex() != ';')
yyerror("RETURN NEXT cannot have a parameter in function with OUT parameters");
new->retvarno = plpgsql_curr_compile->out_param_varno;
}
else if (plpgsql_curr_compile->fn_retistuple)
{
switch (yylex())
{
case T_ROW:
new->retvarno = yylval.row->rowno;
break;
case T_RECORD:
new->retvarno = yylval.rec->recno;
break;
default:
yyerror("RETURN NEXT must specify a record or row variable in function returning tuple");
break;
}
if (yylex() != ';')
yyerror("RETURN NEXT must specify a record or row variable in function returning tuple");
}
else
new->expr = plpgsql_read_expression(';', ";");
return (PLpgSQL_stmt *)fetch; return (PLpgSQL_stmt *) new;
} }
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/pl/plpgsql/src/scan.l,v 1.52 2006/06/16 23:29:27 tgl Exp $ * $PostgreSQL: pgsql/src/pl/plpgsql/src/scan.l,v 1.53 2006/08/14 00:46:53 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -36,8 +36,6 @@ static int scanner_functype; ...@@ -36,8 +36,6 @@ static int scanner_functype;
static bool scanner_typereported; static bool scanner_typereported;
static int pushback_token; static int pushback_token;
static bool have_pushback_token; static bool have_pushback_token;
static int lookahead_token;
static bool have_lookahead_token;
static const char *cur_line_start; static const char *cur_line_start;
static int cur_line_num; static int cur_line_num;
static char *dolqstart; /* current $foo$ quote start string */ static char *dolqstart; /* current $foo$ quote start string */
...@@ -346,53 +344,25 @@ dump { return O_DUMP; } ...@@ -346,53 +344,25 @@ dump { return O_DUMP; }
/* /*
* This is the yylex routine called from outside. It exists to provide * This is the yylex routine called from outside. It exists to provide
* a pushback facility, as well as to allow us to parse syntax that * a one-token pushback facility. Beware of trying to make it do more:
* requires more than one token of lookahead. * for the most part, plpgsql's gram.y assumes that yytext is in step
* with the "current token".
*/ */
int int
plpgsql_yylex(void) plpgsql_yylex(void)
{ {
int cur_token;
if (have_pushback_token) if (have_pushback_token)
{ {
have_pushback_token = false; have_pushback_token = false;
cur_token = pushback_token; return pushback_token;
}
else if (have_lookahead_token)
{
have_lookahead_token = false;
cur_token = lookahead_token;
}
else
cur_token = yylex();
/* Do we need to look ahead for a possible multiword token? */
switch (cur_token)
{
/* RETURN NEXT must be reduced to a single token */
case K_RETURN:
if (!have_lookahead_token)
{
lookahead_token = yylex();
have_lookahead_token = true;
}
if (lookahead_token == K_NEXT)
{
have_lookahead_token = false;
cur_token = K_RETURN_NEXT;
}
break;
default:
break;
} }
return yylex();
return cur_token;
} }
/* /*
* Push back a single token to be re-read by next plpgsql_yylex() call. * Push back a single token to be re-read by next plpgsql_yylex() call.
*
* NOTE: this does not cause yytext to "back up".
*/ */
void void
plpgsql_push_back_token(int token) plpgsql_push_back_token(int token)
...@@ -494,7 +464,6 @@ plpgsql_scanner_init(const char *str, int functype) ...@@ -494,7 +464,6 @@ plpgsql_scanner_init(const char *str, int functype)
scanner_typereported = false; scanner_typereported = false;
have_pushback_token = false; have_pushback_token = false;
have_lookahead_token = false;
cur_line_start = scanbuf; cur_line_start = scanbuf;
cur_line_num = 1; cur_line_num = 1;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment