Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 8830ce5

Browse files
committed
Tweak plpgsql's expression reader to be smarter about parentheses and
to give more useful error messages. Stephen Szabo's example of this morning ('loop' used as a variable name inside a subselect) works correctly now, and a FOR that is misinterpreted as an integer FOR will draw 'missing .. at end of SQL expression', which is at least marginally helpful.
1 parent d1a2a01 commit 8830ce5

File tree

2 files changed

+68
-84
lines changed

2 files changed

+68
-84
lines changed

src/pl/plpgsql/src/gram.y

Lines changed: 66 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* procedural language
55
*
66
* IDENTIFICATION
7-
* $Header: /cvsroot/pgsql/src/pl/plpgsql/src/gram.y,v 1.28 2001/11/15 23:31:09 tgl Exp $
7+
* $Header: /cvsroot/pgsql/src/pl/plpgsql/src/gram.y,v 1.29 2001/11/29 22:57:37 tgl Exp $
88
*
99
* This software is copyrighted by Jan Wieck - Hamburg.
1010
*
@@ -39,7 +39,11 @@
3939
#include "plpgsql.h"
4040

4141

42-
static PLpgSQL_expr *read_sqlstmt(int until, char *s, char *sqlstart);
42+
static PLpgSQL_expr *read_sql_construct(int until,
43+
const char *expected,
44+
bool isexpression,
45+
const char *sqlstart);
46+
static PLpgSQL_expr *read_sql_stmt(const char *sqlstart);
4347
static PLpgSQL_type *read_datatype(int tok);
4448
static PLpgSQL_stmt *make_select_stmt(void);
4549
static PLpgSQL_stmt *make_fetch_stmt(void);
@@ -407,7 +411,7 @@ decl_cursor_query :
407411
PLpgSQL_expr *query;
408412

409413
plpgsql_ns_setlocal(false);
410-
query = plpgsql_read_expression(';', ";");
414+
query = read_sql_stmt("SELECT ");
411415
plpgsql_ns_setlocal(true);
412416

413417
$$ = query;
@@ -1002,74 +1006,20 @@ fori_varname : T_VARIABLE
10021006

10031007
fori_lower :
10041008
{
1005-
int tok;
1006-
int lno;
1007-
PLpgSQL_dstring ds;
1008-
int nparams = 0;
1009-
int params[1024];
1010-
char buf[32];
1011-
PLpgSQL_expr *expr;
1012-
int firsttok = 1;
1013-
1014-
lno = yylineno;
1015-
plpgsql_dstring_init(&ds);
1016-
plpgsql_dstring_append(&ds, "SELECT ");
1009+
int tok;
10171010

1018-
$$.reverse = 0;
1019-
while((tok = yylex()) != K_DOTDOT)
1011+
tok = yylex();
1012+
if (tok == K_REVERSE)
10201013
{
1021-
if (firsttok)
1022-
{
1023-
firsttok = 0;
1024-
if (tok == K_REVERSE)
1025-
{
1026-
$$.reverse = 1;
1027-
continue;
1028-
}
1029-
}
1030-
if (tok == ';') break;
1031-
if (plpgsql_SpaceScanned)
1032-
plpgsql_dstring_append(&ds, " ");
1033-
switch (tok)
1034-
{
1035-
case T_VARIABLE:
1036-
params[nparams] = yylval.var->varno;
1037-
sprintf(buf, " $%d ", ++nparams);
1038-
plpgsql_dstring_append(&ds, buf);
1039-
break;
1040-
1041-
case T_RECFIELD:
1042-
params[nparams] = yylval.recfield->rfno;
1043-
sprintf(buf, " $%d ", ++nparams);
1044-
plpgsql_dstring_append(&ds, buf);
1045-
break;
1046-
1047-
case T_TGARGV:
1048-
params[nparams] = yylval.trigarg->dno;
1049-
sprintf(buf, " $%d ", ++nparams);
1050-
plpgsql_dstring_append(&ds, buf);
1051-
break;
1052-
1053-
default:
1054-
if (tok == 0)
1055-
{
1056-
plpgsql_error_lineno = lno;
1057-
elog(ERROR, "missing .. to terminate lower bound of for loop");
1058-
}
1059-
plpgsql_dstring_append(&ds, yytext);
1060-
break;
1061-
}
1014+
$$.reverse = 1;
1015+
}
1016+
else
1017+
{
1018+
$$.reverse = 0;
1019+
plpgsql_push_back_token(tok);
10621020
}
10631021

1064-
expr = malloc(sizeof(PLpgSQL_expr) + sizeof(int) * nparams - sizeof(int));
1065-
expr->dtype = PLPGSQL_DTYPE_EXPR;
1066-
expr->query = strdup(plpgsql_dstring_get(&ds));
1067-
expr->plan = NULL;
1068-
expr->nparams = nparams;
1069-
while(nparams-- > 0)
1070-
expr->params[nparams] = params[nparams];
1071-
plpgsql_dstring_free(&ds);
1072-
$$.expr = expr;
1022+
$$.expr = plpgsql_read_expression(K_DOTDOT, "..");
10731023
}
10741024

10751025
stmt_fors : opt_label K_FOR lno fors_target K_IN K_SELECT expr_until_loop loop_body
@@ -1308,7 +1258,7 @@ stmt_execsql : execsql_start lno
13081258
new = malloc(sizeof(PLpgSQL_stmt_execsql));
13091259
new->cmd_type = PLPGSQL_STMT_EXECSQL;
13101260
new->lineno = $2;
1311-
new->sqlstmt = read_sqlstmt(';', ";", $1);
1261+
new->sqlstmt = read_sql_stmt($1);
13121262

13131263
$$ = (PLpgSQL_stmt *)new;
13141264
}
@@ -1353,11 +1303,11 @@ stmt_open : K_OPEN lno cursor_varptr
13531303
switch (tok)
13541304
{
13551305
case K_SELECT:
1356-
new->query = plpgsql_read_expression(';', ";");
1306+
new->query = read_sql_stmt("SELECT ");
13571307
break;
13581308

13591309
case K_EXECUTE:
1360-
new->dynquery = plpgsql_read_expression(';', ";");
1310+
new->dynquery = read_sql_stmt("SELECT ");
13611311
break;
13621312

13631313
default:
@@ -1380,7 +1330,7 @@ stmt_open : K_OPEN lno cursor_varptr
13801330
elog(ERROR, "cursor %s has arguments", $3->refname);
13811331
}
13821332

1383-
new->argquery = read_sqlstmt(';', ";", "SELECT ");
1333+
new->argquery = read_sql_stmt("SELECT ");
13841334
/* Remove the trailing right paren,
13851335
* because we want "select 1, 2", not
13861336
* "select (1, 2)".
@@ -1521,39 +1471,71 @@ lno :
15211471

15221472

15231473
PLpgSQL_expr *
1524-
plpgsql_read_expression (int until, char *s)
1474+
plpgsql_read_expression(int until, const char *expected)
15251475
{
1526-
return read_sqlstmt(until, s, "SELECT ");
1476+
return read_sql_construct(until, expected, true, "SELECT ");
15271477
}
15281478

1479+
static PLpgSQL_expr *
1480+
read_sql_stmt(const char *sqlstart)
1481+
{
1482+
return read_sql_construct(';', ";", false, sqlstart);
1483+
}
15291484

15301485
static PLpgSQL_expr *
1531-
read_sqlstmt (int until, char *s, char *sqlstart)
1486+
read_sql_construct(int until,
1487+
const char *expected,
1488+
bool isexpression,
1489+
const char *sqlstart)
15321490
{
15331491
int tok;
15341492
int lno;
15351493
PLpgSQL_dstring ds;
1494+
int parenlevel = 0;
15361495
int nparams = 0;
15371496
int params[1024];
15381497
char buf[32];
15391498
PLpgSQL_expr *expr;
15401499

15411500
lno = yylineno;
15421501
plpgsql_dstring_init(&ds);
1543-
plpgsql_dstring_append(&ds, sqlstart);
1502+
plpgsql_dstring_append(&ds, (char *) sqlstart);
15441503

1545-
while((tok = yylex()) != until)
1504+
for (;;)
15461505
{
1547-
if (tok == ';') break;
1506+
tok = yylex();
1507+
if (tok == '(')
1508+
parenlevel++;
1509+
else if (tok == ')')
1510+
{
1511+
parenlevel--;
1512+
if (parenlevel < 0)
1513+
elog(ERROR, "mismatched parentheses");
1514+
}
1515+
else if (parenlevel == 0 && tok == until)
1516+
break;
1517+
/*
1518+
* End of function definition is an error, and we don't expect to
1519+
* hit a semicolon either (unless it's the until symbol, in which
1520+
* case we should have fallen out above).
1521+
*/
1522+
if (tok == 0 || tok == ';')
1523+
{
1524+
plpgsql_error_lineno = lno;
1525+
if (parenlevel != 0)
1526+
elog(ERROR, "mismatched parentheses");
1527+
if (isexpression)
1528+
elog(ERROR, "missing %s at end of SQL expression",
1529+
expected);
1530+
else
1531+
elog(ERROR, "missing %s at end of SQL statement",
1532+
expected);
1533+
break;
1534+
}
15481535
if (plpgsql_SpaceScanned)
15491536
plpgsql_dstring_append(&ds, " ");
15501537
switch (tok)
15511538
{
1552-
case 0:
1553-
plpgsql_error_lineno = lno;
1554-
elog(ERROR, "missing %s at end of SQL statement", s);
1555-
break;
1556-
15571539
case T_VARIABLE:
15581540
params[nparams] = yylval.var->varno;
15591541
sprintf(buf, " $%d ", ++nparams);
@@ -1618,6 +1600,8 @@ read_datatype(int tok)
16181600
if (tok == 0)
16191601
{
16201602
plpgsql_error_lineno = lno;
1603+
if (parenlevel != 0)
1604+
elog(ERROR, "mismatched parentheses");
16211605
elog(ERROR, "incomplete datatype declaration");
16221606
}
16231607
/* Possible followers for datatype in a declaration */

src/pl/plpgsql/src/plpgsql.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* procedural language
44
*
55
* IDENTIFICATION
6-
* $Header: /cvsroot/pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.23 2001/11/15 23:31:09 tgl Exp $
6+
* $Header: /cvsroot/pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.24 2001/11/29 22:57:37 tgl Exp $
77
*
88
* This software is copyrighted by Jan Wieck - Hamburg.
99
*
@@ -606,7 +606,7 @@ extern void plpgsql_dumptree(PLpgSQL_function * func);
606606
* Externs in gram.y and scan.l
607607
* ----------
608608
*/
609-
extern PLpgSQL_expr *plpgsql_read_expression(int until, char *s);
609+
extern PLpgSQL_expr *plpgsql_read_expression(int until, const char *expected);
610610
extern int plpgsql_yyparse(void);
611611
extern int plpgsql_base_yylex(void);
612612
extern int plpgsql_yylex(void);

0 commit comments

Comments
 (0)