Commit 8830ce54 authored by Tom Lane's avatar Tom Lane

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.
parent d1a2a01f
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
* procedural language * procedural language
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/pl/plpgsql/src/gram.y,v 1.28 2001/11/15 23:31:09 tgl Exp $ * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/gram.y,v 1.29 2001/11/29 22:57:37 tgl Exp $
* *
* This software is copyrighted by Jan Wieck - Hamburg. * This software is copyrighted by Jan Wieck - Hamburg.
* *
...@@ -39,7 +39,11 @@ ...@@ -39,7 +39,11 @@
#include "plpgsql.h" #include "plpgsql.h"
static PLpgSQL_expr *read_sqlstmt(int until, char *s, char *sqlstart); static PLpgSQL_expr *read_sql_construct(int until,
const char *expected,
bool isexpression,
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(void);
static PLpgSQL_stmt *make_fetch_stmt(void); static PLpgSQL_stmt *make_fetch_stmt(void);
...@@ -407,7 +411,7 @@ decl_cursor_query : ...@@ -407,7 +411,7 @@ decl_cursor_query :
PLpgSQL_expr *query; PLpgSQL_expr *query;
plpgsql_ns_setlocal(false); plpgsql_ns_setlocal(false);
query = plpgsql_read_expression(';', ";"); query = read_sql_stmt("SELECT ");
plpgsql_ns_setlocal(true); plpgsql_ns_setlocal(true);
$$ = query; $$ = query;
...@@ -1002,74 +1006,20 @@ fori_varname : T_VARIABLE ...@@ -1002,74 +1006,20 @@ fori_varname : T_VARIABLE
fori_lower : fori_lower :
{ {
int tok; int tok;
int lno;
PLpgSQL_dstring ds;
int nparams = 0;
int params[1024];
char buf[32];
PLpgSQL_expr *expr;
int firsttok = 1;
lno = yylineno;
plpgsql_dstring_init(&ds);
plpgsql_dstring_append(&ds, "SELECT ");
$$.reverse = 0; tok = yylex();
while((tok = yylex()) != K_DOTDOT) if (tok == K_REVERSE)
{ {
if (firsttok) $$.reverse = 1;
{ }
firsttok = 0; else
if (tok == K_REVERSE) {
{ $$.reverse = 0;
$$.reverse = 1; plpgsql_push_back_token(tok);
continue;
}
}
if (tok == ';') break;
if (plpgsql_SpaceScanned)
plpgsql_dstring_append(&ds, " ");
switch (tok)
{
case T_VARIABLE:
params[nparams] = yylval.var->varno;
sprintf(buf, " $%d ", ++nparams);
plpgsql_dstring_append(&ds, buf);
break;
case T_RECFIELD:
params[nparams] = yylval.recfield->rfno;
sprintf(buf, " $%d ", ++nparams);
plpgsql_dstring_append(&ds, buf);
break;
case T_TGARGV:
params[nparams] = yylval.trigarg->dno;
sprintf(buf, " $%d ", ++nparams);
plpgsql_dstring_append(&ds, buf);
break;
default:
if (tok == 0)
{
plpgsql_error_lineno = lno;
elog(ERROR, "missing .. to terminate lower bound of for loop");
}
plpgsql_dstring_append(&ds, yytext);
break;
}
} }
expr = malloc(sizeof(PLpgSQL_expr) + sizeof(int) * nparams - sizeof(int)); $$.expr = plpgsql_read_expression(K_DOTDOT, "..");
expr->dtype = PLPGSQL_DTYPE_EXPR;
expr->query = strdup(plpgsql_dstring_get(&ds));
expr->plan = NULL;
expr->nparams = nparams;
while(nparams-- > 0)
expr->params[nparams] = params[nparams];
plpgsql_dstring_free(&ds);
$$.expr = expr;
} }
stmt_fors : opt_label K_FOR lno fors_target K_IN K_SELECT expr_until_loop loop_body 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 ...@@ -1308,7 +1258,7 @@ stmt_execsql : execsql_start lno
new = malloc(sizeof(PLpgSQL_stmt_execsql)); new = malloc(sizeof(PLpgSQL_stmt_execsql));
new->cmd_type = PLPGSQL_STMT_EXECSQL; new->cmd_type = PLPGSQL_STMT_EXECSQL;
new->lineno = $2; new->lineno = $2;
new->sqlstmt = read_sqlstmt(';', ";", $1); new->sqlstmt = read_sql_stmt($1);
$$ = (PLpgSQL_stmt *)new; $$ = (PLpgSQL_stmt *)new;
} }
...@@ -1353,11 +1303,11 @@ stmt_open : K_OPEN lno cursor_varptr ...@@ -1353,11 +1303,11 @@ stmt_open : K_OPEN lno cursor_varptr
switch (tok) switch (tok)
{ {
case K_SELECT: case K_SELECT:
new->query = plpgsql_read_expression(';', ";"); new->query = read_sql_stmt("SELECT ");
break; break;
case K_EXECUTE: case K_EXECUTE:
new->dynquery = plpgsql_read_expression(';', ";"); new->dynquery = read_sql_stmt("SELECT ");
break; break;
default: default:
...@@ -1380,7 +1330,7 @@ stmt_open : K_OPEN lno cursor_varptr ...@@ -1380,7 +1330,7 @@ stmt_open : K_OPEN lno cursor_varptr
elog(ERROR, "cursor %s has arguments", $3->refname); elog(ERROR, "cursor %s has arguments", $3->refname);
} }
new->argquery = read_sqlstmt(';', ";", "SELECT "); new->argquery = read_sql_stmt("SELECT ");
/* Remove the trailing right paren, /* Remove the trailing right paren,
* because we want "select 1, 2", not * because we want "select 1, 2", not
* "select (1, 2)". * "select (1, 2)".
...@@ -1521,18 +1471,27 @@ lno : ...@@ -1521,18 +1471,27 @@ lno :
PLpgSQL_expr * PLpgSQL_expr *
plpgsql_read_expression (int until, char *s) plpgsql_read_expression(int until, const char *expected)
{ {
return read_sqlstmt(until, s, "SELECT "); return read_sql_construct(until, expected, true, "SELECT ");
} }
static PLpgSQL_expr *
read_sql_stmt(const char *sqlstart)
{
return read_sql_construct(';', ";", false, sqlstart);
}
static PLpgSQL_expr * static PLpgSQL_expr *
read_sqlstmt (int until, char *s, char *sqlstart) read_sql_construct(int until,
const char *expected,
bool isexpression,
const char *sqlstart)
{ {
int tok; int tok;
int lno; int lno;
PLpgSQL_dstring ds; PLpgSQL_dstring ds;
int parenlevel = 0;
int nparams = 0; int nparams = 0;
int params[1024]; int params[1024];
char buf[32]; char buf[32];
...@@ -1540,20 +1499,43 @@ read_sqlstmt (int until, char *s, char *sqlstart) ...@@ -1540,20 +1499,43 @@ read_sqlstmt (int until, char *s, char *sqlstart)
lno = yylineno; lno = yylineno;
plpgsql_dstring_init(&ds); plpgsql_dstring_init(&ds);
plpgsql_dstring_append(&ds, sqlstart); plpgsql_dstring_append(&ds, (char *) sqlstart);
while((tok = yylex()) != until) for (;;)
{ {
if (tok == ';') break; tok = yylex();
if (tok == '(')
parenlevel++;
else if (tok == ')')
{
parenlevel--;
if (parenlevel < 0)
elog(ERROR, "mismatched parentheses");
}
else if (parenlevel == 0 && tok == until)
break;
/*
* End of function definition is an error, and we don't expect to
* hit a semicolon either (unless it's the until symbol, in which
* case we should have fallen out above).
*/
if (tok == 0 || tok == ';')
{
plpgsql_error_lineno = lno;
if (parenlevel != 0)
elog(ERROR, "mismatched parentheses");
if (isexpression)
elog(ERROR, "missing %s at end of SQL expression",
expected);
else
elog(ERROR, "missing %s at end of SQL statement",
expected);
break;
}
if (plpgsql_SpaceScanned) if (plpgsql_SpaceScanned)
plpgsql_dstring_append(&ds, " "); plpgsql_dstring_append(&ds, " ");
switch (tok) switch (tok)
{ {
case 0:
plpgsql_error_lineno = lno;
elog(ERROR, "missing %s at end of SQL statement", s);
break;
case T_VARIABLE: case T_VARIABLE:
params[nparams] = yylval.var->varno; params[nparams] = yylval.var->varno;
sprintf(buf, " $%d ", ++nparams); sprintf(buf, " $%d ", ++nparams);
...@@ -1618,6 +1600,8 @@ read_datatype(int tok) ...@@ -1618,6 +1600,8 @@ read_datatype(int tok)
if (tok == 0) if (tok == 0)
{ {
plpgsql_error_lineno = lno; plpgsql_error_lineno = lno;
if (parenlevel != 0)
elog(ERROR, "mismatched parentheses");
elog(ERROR, "incomplete datatype declaration"); elog(ERROR, "incomplete datatype declaration");
} }
/* Possible followers for datatype in a declaration */ /* Possible followers for datatype in a declaration */
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
* procedural language * procedural language
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.23 2001/11/15 23:31:09 tgl Exp $ * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.24 2001/11/29 22:57:37 tgl Exp $
* *
* This software is copyrighted by Jan Wieck - Hamburg. * This software is copyrighted by Jan Wieck - Hamburg.
* *
...@@ -606,7 +606,7 @@ extern void plpgsql_dumptree(PLpgSQL_function * func); ...@@ -606,7 +606,7 @@ extern void plpgsql_dumptree(PLpgSQL_function * func);
* Externs in gram.y and scan.l * Externs in gram.y and scan.l
* ---------- * ----------
*/ */
extern PLpgSQL_expr *plpgsql_read_expression(int until, char *s); extern PLpgSQL_expr *plpgsql_read_expression(int until, const char *expected);
extern int plpgsql_yyparse(void); extern int plpgsql_yyparse(void);
extern int plpgsql_base_yylex(void); extern int plpgsql_base_yylex(void);
extern int plpgsql_yylex(void); extern int plpgsql_yylex(void);
......
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