@ -9,7 +9,7 @@
*
*
*
*
* IDENTIFICATION
* IDENTIFICATION
* $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.108 2008/01/01 19:46:00 momjian Exp $
* $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.109 2008/04/01 03:51:09 tgl Exp $
*
*
*-------------------------------------------------------------------------
*-------------------------------------------------------------------------
*/
*/
@ -21,11 +21,15 @@
static PLpgSQL_expr *read_sql_construct(int until,
static PLpgSQL_expr *read_sql_construct(int until,
int until2,
int until2,
int until3,
const char *expected,
const char *expected,
const char *sqlstart,
const char *sqlstart,
bool isexpression,
bool isexpression,
bool valid_sql,
bool valid_sql,
int *endtoken);
int *endtoken);
static PLpgSQL_expr *read_sql_expression2(int until, int until2,
const char *expected,
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_execsql_stmt(const char *sqlstart, int lineno);
static PLpgSQL_stmt *make_execsql_stmt(const char *sqlstart, int lineno);
@ -200,6 +204,7 @@ static void check_labels(const char *start_label,
%token K_THEN
%token K_THEN
%token K_TO
%token K_TO
%token K_TYPE
%token K_TYPE
%token K_USING
%token K_WARNING
%token K_WARNING
%token K_WHEN
%token K_WHEN
%token K_WHILE
%token K_WHILE
@ -892,8 +897,11 @@ for_control :
{
{
PLpgSQL_stmt_dynfors *new;
PLpgSQL_stmt_dynfors *new;
PLpgSQL_expr *expr;
PLpgSQL_expr *expr;
int term;
expr = plpgsql_read_expression(K_LOOP, "LOOP");
expr = read_sql_expression2(K_LOOP, K_USING,
"LOOP or USING",
&term);
new = palloc0(sizeof(PLpgSQL_stmt_dynfors));
new = palloc0(sizeof(PLpgSQL_stmt_dynfors));
new->cmd_type = PLPGSQL_STMT_DYNFORS;
new->cmd_type = PLPGSQL_STMT_DYNFORS;
@ -921,6 +929,17 @@ for_control :
}
}
new->query = expr;
new->query = expr;
if (term == K_USING)
{
do
{
expr = read_sql_expression2(',', K_LOOP,
", or LOOP",
&term);
new->params = lappend(new->params, expr);
} while (term == ',');
}
$$ = (PLpgSQL_stmt *) new;
$$ = (PLpgSQL_stmt *) new;
}
}
else
else
@ -954,6 +973,7 @@ for_control :
*/
*/
expr1 = read_sql_construct(K_DOTDOT,
expr1 = read_sql_construct(K_DOTDOT,
K_LOOP,
K_LOOP,
0,
"LOOP",
"LOOP",
"SELECT ",
"SELECT ",
true,
true,
@ -973,17 +993,14 @@ for_control :
check_sql_expr(expr1->query);
check_sql_expr(expr1->query);
/* Read and check the second one */
/* Read and check the second one */
expr2 = read_sql_construct(K_LOOP,
expr2 = read_sql_expression2(K_LOOP, K_BY,
K_BY,
"LOOP",
"LOOP",
"SELECT ",
true,
true,
&tok);
&tok);
/* Get the BY clause if any */
/* Get the BY clause if any */
if (tok == K_BY)
if (tok == K_BY)
expr_by = plpgsql_read_expression(K_LOOP, "LOOP");
expr_by = plpgsql_read_expression(K_LOOP,
"LOOP");
else
else
expr_by = NULL;
expr_by = NULL;
@ -1216,19 +1233,16 @@ stmt_raise : K_RAISE lno raise_level raise_msg
yyerror("syntax error");
yyerror("syntax error");
if (tok == ',')
if (tok == ',')
{
do
{
{
PLpgSQL_expr *expr;
PLpgSQL_expr *expr;
int term;
for (;;)
expr = read_sql_expression2(',', ';',
{
", or ;",
expr = read_sql_construct(',', ';', ", or ;",
&tok);
"SELECT ",
true, true, &term);
new->params = lappend(new->params, expr);
new->params = lappend(new->params, expr);
if (term == ';')
} while (tok == ',');
break;
}
}
}
$$ = (PLpgSQL_stmt *)new;
$$ = (PLpgSQL_stmt *)new;
@ -1307,7 +1321,8 @@ stmt_dynexecute : K_EXECUTE lno
PLpgSQL_expr *expr;
PLpgSQL_expr *expr;
int endtoken;
int endtoken;
expr = read_sql_construct(K_INTO, ';', "INTO|;",
expr = read_sql_construct(K_INTO, K_USING, ';',
"INTO or USING or ;",
"SELECT ",
"SELECT ",
true, true, &endtoken);
true, true, &endtoken);
@ -1319,16 +1334,30 @@ stmt_dynexecute : K_EXECUTE lno
new->strict = false;
new->strict = false;
new->rec = NULL;
new->rec = NULL;
new->row = NULL;
new->row = NULL;
new->params = NIL;
/* If we found "INTO", collect the argument */
/* If we found "INTO", collect the argument */
if (endtoken == K_INTO)
if (endtoken == K_INTO)
{
{
new->into = true;
new->into = true;
read_into_target(&new->rec, &new->row, &new->strict);
read_into_target(&new->rec, &new->row, &new->strict);
if (yylex() != ';')
endtoken = yylex();
if (endtoken != ';' && endtoken != K_USING)
yyerror("syntax error");
yyerror("syntax error");
}
}
/* If we found "USING", collect the argument(s) */
if (endtoken == K_USING)
{
do
{
expr = read_sql_expression2(',', ';',
", or ;",
&endtoken);
new->params = lappend(new->params, expr);
} while (endtoken == ',');
}
$$ = (PLpgSQL_stmt *)new;
$$ = (PLpgSQL_stmt *)new;
}
}
;
;
@ -1730,16 +1759,29 @@ assign_expr_param(int dno, int *params, int *nparams)
}
}
/* Convenience routine to read an expression with one possible terminator */
PLpgSQL_expr *
PLpgSQL_expr *
plpgsql_read_expression(int until, const char *expected)
plpgsql_read_expression(int until, const char *expected)
{
{
return read_sql_construct(until, 0, expected, "SELECT ", true, true, NULL);
return read_sql_construct(until, 0, 0, expected,
"SELECT ", true, true, NULL);
}
/* Convenience routine to read an expression with two possible terminators */
static PLpgSQL_expr *
read_sql_expression2(int until, int until2, const char *expected,
int *endtoken)
{
return read_sql_construct(until, until2, 0, expected,
"SELECT ", true, true, endtoken);
}
}
/* Convenience routine to read a SQL statement that must end with ';' */
static PLpgSQL_expr *
static PLpgSQL_expr *
read_sql_stmt(const char *sqlstart)
read_sql_stmt(const char *sqlstart)
{
{
return read_sql_construct(';', 0, ";", sqlstart, false, true, NULL);
return read_sql_construct(';', 0, 0, ";",
sqlstart, false, true, NULL);
}
}
/*
/*
@ -1747,16 +1789,18 @@ read_sql_stmt(const char *sqlstart)
*
*
* until: token code for expected terminator
* until: token code for expected terminator
* until2: token code for alternate terminator (pass 0 if none)
* until2: token code for alternate terminator (pass 0 if none)
* until3: token code for another alternate terminator (pass 0 if none)
* expected: text to use in complaining that terminator was not found
* expected: text to use in complaining that terminator was not found
* sqlstart: text to prefix to the accumulated SQL text
* sqlstart: text to prefix to the accumulated SQL text
* isexpression: whether to say we're reading an "expression" or a "statement"
* isexpression: whether to say we're reading an "expression" or a "statement"
* valid_sql: whether to check the syntax of the expr (prefixed with sqlstart)
* valid_sql: whether to check the syntax of the expr (prefixed with sqlstart)
* endtoken: if not NULL, ending token is stored at *endtoken
* endtoken: if not NULL, ending token is stored at *endtoken
* (this is only interesting if until2 isn't zero)
* (this is only interesting if until2 or until3 isn't zero)
*/
*/
static PLpgSQL_expr *
static PLpgSQL_expr *
read_sql_construct(int until,
read_sql_construct(int until,
int until2,
int until2,
int until3,
const char *expected,
const char *expected,
const char *sqlstart,
const char *sqlstart,
bool isexpression,
bool isexpression,
@ -1783,6 +1827,8 @@ read_sql_construct(int until,
break;
break;
if (tok == until2 && parenlevel == 0)
if (tok == until2 && parenlevel == 0)
break;
break;
if (tok == until3 && parenlevel == 0)
break;
if (tok == '(' || tok == '[')
if (tok == '(' || tok == '[')
parenlevel++;
parenlevel++;
else if (tok == ')' || tok == ']')
else if (tok == ')' || tok == ']')
@ -2066,15 +2112,17 @@ read_fetch_direction(void)
else if (pg_strcasecmp(yytext, "absolute") == 0)
else if (pg_strcasecmp(yytext, "absolute") == 0)
{
{
fetch->direction = FETCH_ABSOLUTE;
fetch->direction = FETCH_ABSOLUTE;
fetch->expr = read_sql_construct(K_FROM, K_IN, "FROM or IN",
fetch->expr = read_sql_expression2(K_FROM, K_IN,
"SELECT ", true, true, NULL);
"FROM or IN",
NULL);
check_FROM = false;
check_FROM = false;
}
}
else if (pg_strcasecmp(yytext, "relative") == 0)
else if (pg_strcasecmp(yytext, "relative") == 0)
{
{
fetch->direction = FETCH_RELATIVE;
fetch->direction = FETCH_RELATIVE;
fetch->expr = read_sql_construct(K_FROM, K_IN, "FROM or IN",
fetch->expr = read_sql_expression2(K_FROM, K_IN,
"SELECT ", true, true, NULL);
"FROM or IN",
NULL);
check_FROM = false;
check_FROM = false;
}
}
else if (pg_strcasecmp(yytext, "forward") == 0)
else if (pg_strcasecmp(yytext, "forward") == 0)
@ -2088,8 +2136,9 @@ read_fetch_direction(void)
else if (tok != T_SCALAR)
else if (tok != T_SCALAR)
{
{
plpgsql_push_back_token(tok);
plpgsql_push_back_token(tok);
fetch->expr = read_sql_construct(K_FROM, K_IN, "FROM or IN",
fetch->expr = read_sql_expression2(K_FROM, K_IN,
"SELECT ", true, true, NULL);
"FROM or IN",
NULL);
check_FROM = false;
check_FROM = false;
}
}
else
else
@ -2233,7 +2282,7 @@ make_return_query_stmt(int lineno)
new = palloc0(sizeof(PLpgSQL_stmt_return_query));
new = palloc0(sizeof(PLpgSQL_stmt_return_query));
new->cmd_type = PLPGSQL_STMT_RETURN_QUERY;
new->cmd_type = PLPGSQL_STMT_RETURN_QUERY;
new->lineno = lineno;
new->lineno = lineno;
new->query = read_sql_construct(';', 0, ")", "", false, true, NULL );
new->query = read_sql_stmt("" );
return (PLpgSQL_stmt *) new;
return (PLpgSQL_stmt *) new;
}
}