Rearrange plpgsql parsing to simplify and speed it up a bit.

* Pull the responsibility for %TYPE and %ROWTYPE out of the scanner,
letting read_datatype manage it instead.

* Avoid unnecessary scanner-driven lookups of plpgsql variables in
places where it's not needed, which is actually most of the time;
we do not need it in DECLARE sections nor in text that is a SQL
query or expression.

* Rationalize the set of token types returned by the scanner:
distinguishing T_SCALAR, T_RECORD, T_ROW seems to complicate the grammar
in more places than it simplifies it, so merge these into one
token type T_DATUM; but split T_ERROR into T_DBLWORD and T_TRIPWORD
for clarity and simplicity of later processing.

Some of this will need to be revisited again when we try to make
plpgsql use the core scanner, but this patch gets some of the bigger
stumbling blocks out of the way.
This commit is contained in:
Tom Lane 2009-11-07 00:52:26 +00:00
parent b79f49c780
commit f2b7692e75
5 changed files with 372 additions and 373 deletions

View File

@ -9,7 +9,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.131 2009/11/06 18:37:54 tgl Exp $ * $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.132 2009/11/07 00:52:26 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -109,11 +109,8 @@ static List *read_raise_options(void);
} loop_body; } loop_body;
List *list; List *list;
PLpgSQL_type *dtype; PLpgSQL_type *dtype;
PLpgSQL_datum *scalar; /* a VAR or RECFIELD */ PLpgSQL_datum *datum;
PLpgSQL_variable *variable; /* a VAR, REC, or ROW */
PLpgSQL_var *var; PLpgSQL_var *var;
PLpgSQL_row *row;
PLpgSQL_rec *rec;
PLpgSQL_expr *expr; PLpgSQL_expr *expr;
PLpgSQL_stmt *stmt; PLpgSQL_stmt *stmt;
PLpgSQL_stmt_block *program; PLpgSQL_stmt_block *program;
@ -131,7 +128,7 @@ static List *read_raise_options(void);
%type <boolean> decl_const decl_notnull exit_type %type <boolean> decl_const decl_notnull exit_type
%type <expr> decl_defval decl_cursor_query %type <expr> decl_defval decl_cursor_query
%type <dtype> decl_datatype %type <dtype> decl_datatype
%type <row> decl_cursor_args %type <datum> decl_cursor_args
%type <list> decl_cursor_arglist %type <list> decl_cursor_arglist
%type <nsitem> decl_aliasitem %type <nsitem> decl_aliasitem
%type <str> decl_stmts decl_stmt %type <str> decl_stmts decl_stmt
@ -142,7 +139,7 @@ static List *read_raise_options(void);
%type <ival> assign_var %type <ival> assign_var
%type <var> cursor_variable %type <var> cursor_variable
%type <variable> decl_cursor_arg %type <datum> decl_cursor_arg
%type <forvariable> for_variable %type <forvariable> for_variable
%type <stmt> for_control %type <stmt> for_control
@ -234,12 +231,10 @@ static List *read_raise_options(void);
*/ */
%token T_STRING %token T_STRING
%token T_NUMBER %token T_NUMBER
%token T_SCALAR /* a VAR or RECFIELD */ %token T_DATUM /* a VAR, ROW, REC, or RECFIELD variable */
%token T_ROW %token T_WORD /* unrecognized simple identifier */
%token T_RECORD %token T_DBLWORD /* unrecognized ident.ident */
%token T_DTYPE %token T_TRIPWORD /* unrecognized ident.ident.ident */
%token T_WORD
%token T_ERROR
%token O_OPTION %token O_OPTION
%token O_DUMP %token O_DUMP
@ -294,21 +289,22 @@ pl_block : decl_sect K_BEGIN lno proc_sect exception_sect K_END opt_label
decl_sect : opt_block_label decl_sect : opt_block_label
{ {
plpgsql_ns_setlocal(false); /* done with decls, so resume identifier lookup */
plpgsql_LookupIdentifiers = true;
$$.label = $1; $$.label = $1;
$$.n_initvars = 0; $$.n_initvars = 0;
$$.initvarnos = NULL; $$.initvarnos = NULL;
} }
| opt_block_label decl_start | opt_block_label decl_start
{ {
plpgsql_ns_setlocal(false); plpgsql_LookupIdentifiers = true;
$$.label = $1; $$.label = $1;
$$.n_initvars = 0; $$.n_initvars = 0;
$$.initvarnos = NULL; $$.initvarnos = NULL;
} }
| opt_block_label decl_start decl_stmts | opt_block_label decl_start decl_stmts
{ {
plpgsql_ns_setlocal(false); plpgsql_LookupIdentifiers = true;
if ($3 != NULL) if ($3 != NULL)
$$.label = $3; $$.label = $3;
else else
@ -322,8 +318,11 @@ decl_start : K_DECLARE
{ {
/* Forget any variables created before block */ /* Forget any variables created before block */
plpgsql_add_initdatums(NULL); plpgsql_add_initdatums(NULL);
/* Make variable names be local to block */ /*
plpgsql_ns_setlocal(true); * Disable scanner lookup of identifiers while
* we process the decl_stmts
*/
plpgsql_LookupIdentifiers = false;
} }
; ;
@ -447,13 +446,7 @@ opt_scrollable :
decl_cursor_query : decl_cursor_query :
{ {
PLpgSQL_expr *query; $$ = read_sql_stmt("");
plpgsql_ns_setlocal(false);
query = read_sql_stmt("");
plpgsql_ns_setlocal(true);
$$ = query;
} }
; ;
@ -486,7 +479,7 @@ decl_cursor_args :
list_free($2); list_free($2);
plpgsql_adddatum((PLpgSQL_datum *) new); plpgsql_adddatum((PLpgSQL_datum *) new);
$$ = new; $$ = (PLpgSQL_datum *) new;
} }
; ;
@ -502,26 +495,24 @@ decl_cursor_arglist : decl_cursor_arg
decl_cursor_arg : decl_varname decl_datatype decl_cursor_arg : decl_varname decl_datatype
{ {
$$ = plpgsql_build_variable($1.name, $1.lineno, $$ = (PLpgSQL_datum *)
$2, true); plpgsql_build_variable($1.name, $1.lineno,
$2, true);
} }
; ;
decl_is_for : K_IS | /* Oracle */ decl_is_for : K_IS | /* Oracle */
K_FOR; /* SQL standard */ K_FOR; /* SQL standard */
decl_aliasitem : any_identifier decl_aliasitem : T_WORD
{ {
char *name; char *name[1];
PLpgSQL_nsitem *nsi; PLpgSQL_nsitem *nsi;
/* XXX should allow block-label-qualified names */ plpgsql_convert_ident(yytext, name, 1);
plpgsql_convert_ident($1, &name, 1);
plpgsql_ns_setlocal(false); nsi = plpgsql_ns_lookup(plpgsql_ns_top(), false,
name[0], NULL, NULL,
nsi = plpgsql_ns_lookup(plpgsql_ns_top(),
name, NULL, NULL,
NULL); NULL);
if (nsi == NULL) if (nsi == NULL)
{ {
@ -529,12 +520,34 @@ decl_aliasitem : any_identifier
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT), (errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("variable \"%s\" does not exist", errmsg("variable \"%s\" does not exist",
name))); name[0])));
} }
plpgsql_ns_setlocal(true); pfree(name[0]);
pfree(name); $$ = nsi;
}
| T_DBLWORD
{
char *name[2];
PLpgSQL_nsitem *nsi;
plpgsql_convert_ident(yytext, name, 2);
nsi = plpgsql_ns_lookup(plpgsql_ns_top(), false,
name[0], name[1], NULL,
NULL);
if (nsi == NULL)
{
plpgsql_error_lineno = plpgsql_scanner_lineno();
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("variable \"%s.%s\" does not exist",
name[0], name[1])));
}
pfree(name[0]);
pfree(name[1]);
$$ = nsi; $$ = nsi;
} }
@ -547,24 +560,14 @@ decl_varname : T_WORD
plpgsql_convert_ident(yytext, &name, 1); plpgsql_convert_ident(yytext, &name, 1);
$$.name = name; $$.name = name;
$$.lineno = plpgsql_scanner_lineno(); $$.lineno = plpgsql_scanner_lineno();
}
| T_SCALAR
{
/* /*
* Since the scanner is only searching the topmost * Check to make sure name isn't already declared
* namespace level, getting T_SCALAR etc can only * in the current block.
* happen if the name is already declared in this
* block.
*/ */
yyerror("duplicate declaration"); if (plpgsql_ns_lookup(plpgsql_ns_top(), true,
} name, NULL, NULL,
| T_ROW NULL) != NULL)
{ yyerror("duplicate declaration");
yyerror("duplicate declaration");
}
| T_RECORD
{
yyerror("duplicate declaration");
} }
; ;
@ -595,9 +598,7 @@ decl_defval : ';'
{ $$ = NULL; } { $$ = NULL; }
| decl_defkey | decl_defkey
{ {
plpgsql_ns_setlocal(false);
$$ = plpgsql_read_expression(';', ";"); $$ = plpgsql_read_expression(';', ";");
plpgsql_ns_setlocal(true);
} }
; ;
@ -739,40 +740,32 @@ getdiag_kind : K_ROW_COUNT
} }
; ;
getdiag_target : T_SCALAR getdiag_target : T_DATUM
{ {
check_assignable(yylval.scalar); check_assignable(yylval.datum);
$$ = yylval.scalar->dno; if (yylval.datum->dtype == PLPGSQL_DTYPE_ROW ||
} yylval.datum->dtype == PLPGSQL_DTYPE_REC)
| T_ROW ereport(ERROR,
{ (errcode(ERRCODE_SYNTAX_ERROR),
yyerror("expected an integer variable"); errmsg("\"%s\" is not a scalar variable",
} yytext)));
| T_RECORD $$ = yylval.datum->dno;
{
yyerror("expected an integer variable");
} }
| T_WORD | T_WORD
{ {
yyerror("expected an integer variable"); /* just to give a better message than "syntax error" */
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("\"%s\" is not a known variable",
yytext)));
} }
; ;
assign_var : T_SCALAR assign_var : T_DATUM
{ {
check_assignable(yylval.scalar); check_assignable(yylval.datum);
$$ = yylval.scalar->dno; $$ = yylval.datum->dno;
}
| T_ROW
{
check_assignable((PLpgSQL_datum *) yylval.row);
$$ = yylval.row->dno;
}
| T_RECORD
{
check_assignable((PLpgSQL_datum *) yylval.rec);
$$ = yylval.rec->dno;
} }
| assign_var '[' expr_until_rightbracket | assign_var '[' expr_until_rightbracket
{ {
@ -783,7 +776,7 @@ assign_var : T_SCALAR
new->subscript = $3; new->subscript = $3;
new->arrayparentno = $1; new->arrayparentno = $1;
plpgsql_adddatum((PLpgSQL_datum *)new); plpgsql_adddatum((PLpgSQL_datum *) new);
$$ = new->dno; $$ = new->dno;
} }
@ -1026,13 +1019,13 @@ for_control :
$$ = (PLpgSQL_stmt *) new; $$ = (PLpgSQL_stmt *) new;
} }
else if (tok == T_SCALAR && else if (tok == T_DATUM &&
yylval.scalar->dtype == PLPGSQL_DTYPE_VAR && yylval.datum->dtype == PLPGSQL_DTYPE_VAR &&
((PLpgSQL_var *) yylval.scalar)->datatype->typoid == REFCURSOROID) ((PLpgSQL_var *) yylval.datum)->datatype->typoid == REFCURSOROID)
{ {
/* It's FOR var IN cursor */ /* It's FOR var IN cursor */
PLpgSQL_stmt_forc *new; PLpgSQL_stmt_forc *new;
PLpgSQL_var *cursor = (PLpgSQL_var *) yylval.scalar; PLpgSQL_var *cursor = (PLpgSQL_var *) yylval.datum;
char *varname; char *varname;
new = (PLpgSQL_stmt_forc *) palloc0(sizeof(PLpgSQL_stmt_forc)); new = (PLpgSQL_stmt_forc *) palloc0(sizeof(PLpgSQL_stmt_forc));
@ -1227,20 +1220,36 @@ for_control :
* Note that the non-error result of this case sets *both* $$.scalar and * Note that the non-error result of this case sets *both* $$.scalar and
* $$.row; see the for_control production. * $$.row; see the for_control production.
*/ */
for_variable : T_SCALAR for_variable : T_DATUM
{ {
int tok;
$$.name = pstrdup(yytext); $$.name = pstrdup(yytext);
$$.lineno = plpgsql_scanner_lineno(); $$.lineno = plpgsql_scanner_lineno();
$$.scalar = yylval.scalar; if (yylval.datum->dtype == PLPGSQL_DTYPE_ROW)
$$.rec = NULL; {
$$.row = NULL; $$.scalar = NULL;
/* check for comma-separated list */ $$.rec = NULL;
tok = yylex(); $$.row = (PLpgSQL_row *) yylval.datum;
plpgsql_push_back_token(tok); }
if (tok == ',') else if (yylval.datum->dtype == PLPGSQL_DTYPE_REC)
$$.row = read_into_scalar_list($$.name, $$.scalar); {
$$.scalar = NULL;
$$.rec = (PLpgSQL_rec *) yylval.datum;
$$.row = NULL;
}
else
{
int tok;
$$.scalar = yylval.datum;
$$.rec = NULL;
$$.row = NULL;
/* check for comma-separated list */
tok = yylex();
plpgsql_push_back_token(tok);
if (tok == ',')
$$.row = read_into_scalar_list($$.name,
$$.scalar);
}
} }
| T_WORD | T_WORD
{ {
@ -1259,26 +1268,10 @@ for_variable : T_SCALAR
plpgsql_error_lineno = $$.lineno; plpgsql_error_lineno = $$.lineno;
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR), (errcode(ERRCODE_SYNTAX_ERROR),
errmsg("\"%s\" is not a scalar variable", errmsg("\"%s\" is not a known variable",
$$.name))); $$.name)));
} }
} }
| T_RECORD
{
$$.name = pstrdup(yytext);
$$.lineno = plpgsql_scanner_lineno();
$$.scalar = NULL;
$$.rec = yylval.rec;
$$.row = NULL;
}
| T_ROW
{
$$.name = pstrdup(yytext);
$$.lineno = plpgsql_scanner_lineno();
$$.scalar = NULL;
$$.row = yylval.row;
$$.rec = NULL;
}
; ;
stmt_exit : exit_type lno opt_label opt_exitcond stmt_exit : exit_type lno opt_label opt_exitcond
@ -1484,12 +1477,18 @@ stmt_execsql : execsql_start lno
} }
; ;
/* T_WORD+T_ERROR match any otherwise-unrecognized starting keyword */ /*
* T_WORD+T_DBLWORD+T_TRIPWORD match any initial identifier that is not a
* known plpgsql variable. The latter two cases are probably syntax errors,
* but we'll let the core parser decide that.
*/
execsql_start : K_INSERT execsql_start : K_INSERT
{ $$ = pstrdup(yytext); } { $$ = pstrdup(yytext); }
| T_WORD | T_WORD
{ $$ = pstrdup(yytext); } { $$ = pstrdup(yytext); }
| T_ERROR | T_DBLWORD
{ $$ = pstrdup(yytext); }
| T_TRIPWORD
{ $$ = pstrdup(yytext); } { $$ = pstrdup(yytext); }
; ;
@ -1667,32 +1666,28 @@ stmt_null : K_NULL ';'
} }
; ;
cursor_variable : T_SCALAR cursor_variable : T_DATUM
{ {
if (yylval.scalar->dtype != PLPGSQL_DTYPE_VAR) if (yylval.datum->dtype != PLPGSQL_DTYPE_VAR)
yyerror("cursor variable must be a simple variable"); yyerror("cursor variable must be a simple variable");
if (((PLpgSQL_var *) yylval.scalar)->datatype->typoid != REFCURSOROID) if (((PLpgSQL_var *) yylval.datum)->datatype->typoid != REFCURSOROID)
{ {
plpgsql_error_lineno = plpgsql_scanner_lineno(); plpgsql_error_lineno = plpgsql_scanner_lineno();
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH), (errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("variable \"%s\" must be of type cursor or refcursor", errmsg("variable \"%s\" must be of type cursor or refcursor",
((PLpgSQL_var *) yylval.scalar)->refname))); ((PLpgSQL_var *) yylval.datum)->refname)));
} }
$$ = (PLpgSQL_var *) yylval.scalar; $$ = (PLpgSQL_var *) yylval.datum;
}
| T_ROW
{
yyerror("expected a cursor or refcursor variable");
}
| T_RECORD
{
yyerror("expected a cursor or refcursor variable");
} }
| T_WORD | T_WORD
{ {
yyerror("expected a cursor or refcursor variable"); /* just to give a better message than "syntax error" */
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("\"%s\" is not a known variable",
yytext)));
} }
; ;
@ -1852,21 +1847,13 @@ opt_exitcond : ';'
; ;
/* /*
* need all the options because scanner will have tried to resolve as variable * need both options because scanner will have tried to resolve as variable
*/ */
any_identifier : T_WORD any_identifier : T_WORD
{ {
$$ = yytext; $$ = yytext;
} }
| T_SCALAR | T_DATUM
{
$$ = yytext;
}
| T_RECORD
{
$$ = yytext;
}
| T_ROW
{ {
$$ = yytext; $$ = yytext;
} }
@ -1941,6 +1928,7 @@ read_sql_construct(int until,
int tok; int tok;
int lno; int lno;
StringInfoData ds; StringInfoData ds;
bool save_LookupIdentifiers;
int parenlevel = 0; int parenlevel = 0;
PLpgSQL_expr *expr; PLpgSQL_expr *expr;
@ -1948,6 +1936,10 @@ read_sql_construct(int until,
initStringInfo(&ds); initStringInfo(&ds);
appendStringInfoString(&ds, sqlstart); appendStringInfoString(&ds, sqlstart);
/* no need to lookup identifiers within the SQL text */
save_LookupIdentifiers = plpgsql_LookupIdentifiers;
plpgsql_LookupIdentifiers = false;
for (;;) for (;;)
{ {
tok = yylex(); tok = yylex();
@ -1992,6 +1984,8 @@ read_sql_construct(int until,
appendStringInfoString(&ds, yytext); appendStringInfoString(&ds, yytext);
} }
plpgsql_LookupIdentifiers = save_LookupIdentifiers;
if (endtoken) if (endtoken)
*endtoken = tok; *endtoken = tok;
@ -2019,19 +2013,100 @@ read_datatype(int tok)
bool needspace = false; bool needspace = false;
int parenlevel = 0; int parenlevel = 0;
/* Should always be called with LookupIdentifiers off */
Assert(!plpgsql_LookupIdentifiers);
lno = plpgsql_scanner_lineno(); lno = plpgsql_scanner_lineno();
initStringInfo(&ds);
/* Often there will be a lookahead token, but if not, get one */ /* Often there will be a lookahead token, but if not, get one */
if (tok == YYEMPTY) if (tok == YYEMPTY)
tok = yylex(); tok = yylex();
if (tok == T_DTYPE) /*
* If we have a single, double, or triple identifier, check for %TYPE
* and %ROWTYPE constructs.
*/
if (tok == T_WORD)
{ {
/* lexer found word%TYPE and did its thing already */ appendStringInfoString(&ds, yytext);
return yylval.dtype; tok = yylex();
if (tok == '%')
{
tok = yylex();
if (pg_strcasecmp(yytext, "type") == 0)
{
result = plpgsql_parse_wordtype(ds.data);
if (result)
{
pfree(ds.data);
return result;
}
}
else if (pg_strcasecmp(yytext, "rowtype") == 0)
{
result = plpgsql_parse_wordrowtype(ds.data);
if (result)
{
pfree(ds.data);
return result;
}
}
appendStringInfoString(&ds, " %");
}
needspace = true;
}
else if (tok == T_DBLWORD)
{
appendStringInfoString(&ds, yytext);
tok = yylex();
if (tok == '%')
{
tok = yylex();
if (pg_strcasecmp(yytext, "type") == 0)
{
result = plpgsql_parse_dblwordtype(ds.data);
if (result)
{
pfree(ds.data);
return result;
}
}
else if (pg_strcasecmp(yytext, "rowtype") == 0)
{
result = plpgsql_parse_dblwordrowtype(ds.data);
if (result)
{
pfree(ds.data);
return result;
}
}
appendStringInfoString(&ds, " %");
}
needspace = true;
}
else if (tok == T_TRIPWORD)
{
appendStringInfoString(&ds, yytext);
tok = yylex();
if (tok == '%')
{
tok = yylex();
if (pg_strcasecmp(yytext, "type") == 0)
{
result = plpgsql_parse_tripwordtype(ds.data);
if (result)
{
pfree(ds.data);
return result;
}
}
/* there's no tripword rowtype construct */
appendStringInfoString(&ds, " %");
}
needspace = true;
} }
initStringInfo(&ds);
while (tok != ';') while (tok != ';')
{ {
@ -2080,6 +2155,7 @@ static PLpgSQL_stmt *
make_execsql_stmt(const char *sqlstart, int lineno) make_execsql_stmt(const char *sqlstart, int lineno)
{ {
StringInfoData ds; StringInfoData ds;
bool save_LookupIdentifiers;
PLpgSQL_stmt_execsql *execsql; PLpgSQL_stmt_execsql *execsql;
PLpgSQL_expr *expr; PLpgSQL_expr *expr;
PLpgSQL_row *row = NULL; PLpgSQL_row *row = NULL;
@ -2092,6 +2168,10 @@ make_execsql_stmt(const char *sqlstart, int lineno)
initStringInfo(&ds); initStringInfo(&ds);
appendStringInfoString(&ds, sqlstart); appendStringInfoString(&ds, sqlstart);
/* no need to lookup identifiers within the SQL text */
save_LookupIdentifiers = plpgsql_LookupIdentifiers;
plpgsql_LookupIdentifiers = false;
/* /*
* We have to special-case the sequence INSERT INTO, because we don't want * We have to special-case the sequence INSERT INTO, because we don't want
* that to be taken as an INTO-variables clause. Fortunately, this is the * that to be taken as an INTO-variables clause. Fortunately, this is the
@ -2119,7 +2199,9 @@ make_execsql_stmt(const char *sqlstart, int lineno)
if (have_into) if (have_into)
yyerror("INTO specified more than once"); yyerror("INTO specified more than once");
have_into = true; have_into = true;
plpgsql_LookupIdentifiers = true;
read_into_target(&rec, &row, &have_strict); read_into_target(&rec, &row, &have_strict);
plpgsql_LookupIdentifiers = false;
continue; continue;
} }
@ -2128,6 +2210,8 @@ make_execsql_stmt(const char *sqlstart, int lineno)
appendStringInfoString(&ds, yytext); appendStringInfoString(&ds, yytext);
} }
plpgsql_LookupIdentifiers = save_LookupIdentifiers;
expr = palloc0(sizeof(PLpgSQL_expr)); expr = palloc0(sizeof(PLpgSQL_expr));
expr->dtype = PLPGSQL_DTYPE_EXPR; expr->dtype = PLPGSQL_DTYPE_EXPR;
expr->query = pstrdup(ds.data); expr->query = pstrdup(ds.data);
@ -2233,7 +2317,7 @@ read_fetch_direction(void)
/* empty direction */ /* empty direction */
check_FROM = false; check_FROM = false;
} }
else if (tok == T_SCALAR) else if (tok == T_DATUM)
{ {
/* Assume there's no direction clause and tok is a cursor name */ /* Assume there's no direction clause and tok is a cursor name */
plpgsql_push_back_token(tok); plpgsql_push_back_token(tok);
@ -2245,7 +2329,7 @@ read_fetch_direction(void)
* Assume it's a count expression with no preceding keyword. * Assume it's a count expression with no preceding keyword.
* Note: we allow this syntax because core SQL does, but we don't * Note: we allow this syntax because core SQL does, but we don't
* document it because of the ambiguity with the omitted-direction * document it because of the ambiguity with the omitted-direction
* case. For instance, "MOVE n IN c" will fail if n is a scalar. * case. For instance, "MOVE n IN c" will fail if n is a variable.
* Perhaps this can be improved someday, but it's hardly worth a * Perhaps this can be improved someday, but it's hardly worth a
* lot of work. * lot of work.
*/ */
@ -2342,12 +2426,12 @@ make_return_stmt(int lineno)
/* we allow this to support RETURN NULL in triggers */ /* we allow this to support RETURN NULL in triggers */
break; break;
case T_ROW: case T_DATUM:
new->retvarno = yylval.row->dno; if (yylval.datum->dtype == PLPGSQL_DTYPE_ROW ||
break; yylval.datum->dtype == PLPGSQL_DTYPE_REC)
new->retvarno = yylval.datum->dno;
case T_RECORD: else
new->retvarno = yylval.rec->dno; yyerror("RETURN must specify a record or row variable in function returning row");
break; break;
default: default:
@ -2395,12 +2479,12 @@ make_return_next_stmt(int lineno)
{ {
switch (yylex()) switch (yylex())
{ {
case T_ROW: case T_DATUM:
new->retvarno = yylval.row->dno; if (yylval.datum->dtype == PLPGSQL_DTYPE_ROW ||
break; yylval.datum->dtype == PLPGSQL_DTYPE_REC)
new->retvarno = yylval.datum->dno;
case T_RECORD: else
new->retvarno = yylval.rec->dno; yyerror("RETURN NEXT must specify a record or row variable in function returning row");
break; break;
default: default:
@ -2517,18 +2601,21 @@ read_into_target(PLpgSQL_rec **rec, PLpgSQL_row **row, bool *strict)
switch (tok) switch (tok)
{ {
case T_ROW: case T_DATUM:
*row = yylval.row; if (yylval.datum->dtype == PLPGSQL_DTYPE_ROW)
check_assignable((PLpgSQL_datum *) *row); {
break; check_assignable(yylval.datum);
*row = (PLpgSQL_row *) yylval.datum;
case T_RECORD: }
*rec = yylval.rec; else if (yylval.datum->dtype == PLPGSQL_DTYPE_REC)
check_assignable((PLpgSQL_datum *) *rec); {
break; check_assignable(yylval.datum);
*rec = (PLpgSQL_rec *) yylval.datum;
case T_SCALAR: }
*row = read_into_scalar_list(yytext, yylval.scalar); else
{
*row = read_into_scalar_list(yytext, yylval.datum);
}
break; break;
default: default:
@ -2576,17 +2663,23 @@ read_into_scalar_list(const char *initial_name,
tok = yylex(); tok = yylex();
switch(tok) switch(tok)
{ {
case T_SCALAR: case T_DATUM:
check_assignable(yylval.scalar); check_assignable(yylval.datum);
if (yylval.datum->dtype == PLPGSQL_DTYPE_ROW ||
yylval.datum->dtype == PLPGSQL_DTYPE_REC)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("\"%s\" is not a scalar variable",
yytext)));
fieldnames[nfields] = pstrdup(yytext); fieldnames[nfields] = pstrdup(yytext);
varnos[nfields++] = yylval.scalar->dno; varnos[nfields++] = yylval.datum->dno;
break; break;
default: default:
plpgsql_error_lineno = plpgsql_scanner_lineno(); plpgsql_error_lineno = plpgsql_scanner_lineno();
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR), (errcode(ERRCODE_SYNTAX_ERROR),
errmsg("\"%s\" is not a scalar variable", errmsg("\"%s\" is not a known variable",
yytext))); yytext)));
} }
} }

View File

@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.141 2009/11/06 18:37:54 tgl Exp $ * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.142 2009/11/07 00:52:26 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -1006,7 +1006,7 @@ plpgsql_param_ref(ParseState *pstate, ParamRef *pref)
snprintf(pname, sizeof(pname), "$%d", pref->number); snprintf(pname, sizeof(pname), "$%d", pref->number);
nse = plpgsql_ns_lookup(expr->ns, nse = plpgsql_ns_lookup(expr->ns, false,
pname, NULL, NULL, pname, NULL, NULL,
NULL); NULL);
@ -1120,7 +1120,7 @@ resolve_column_ref(PLpgSQL_expr *expr, ColumnRef *cref)
return NULL; return NULL;
} }
nse = plpgsql_ns_lookup(expr->ns, nse = plpgsql_ns_lookup(expr->ns, false,
name1, name2, name3, name1, name2, name3,
&nnames); &nnames);
@ -1200,7 +1200,7 @@ resolve_column_ref(PLpgSQL_expr *expr, ColumnRef *cref)
} }
break; break;
default: default:
elog(ERROR, "unrecognized plpgsql itemtype"); elog(ERROR, "unrecognized plpgsql itemtype: %d", nse->itemtype);
} }
/* Name format doesn't match the plpgsql variable type */ /* Name format doesn't match the plpgsql variable type */
@ -1259,7 +1259,7 @@ plpgsql_parse_word(const char *word)
/* /*
* Do a lookup in the current namespace stack * Do a lookup in the current namespace stack
*/ */
nse = plpgsql_ns_lookup(plpgsql_ns_top(), nse = plpgsql_ns_lookup(plpgsql_ns_top(), false,
cp[0], NULL, NULL, cp[0], NULL, NULL,
NULL); NULL);
pfree(cp[0]); pfree(cp[0]);
@ -1269,19 +1269,13 @@ plpgsql_parse_word(const char *word)
switch (nse->itemtype) switch (nse->itemtype)
{ {
case PLPGSQL_NSTYPE_VAR: case PLPGSQL_NSTYPE_VAR:
plpgsql_yylval.scalar = plpgsql_Datums[nse->itemno];
return T_SCALAR;
case PLPGSQL_NSTYPE_REC:
plpgsql_yylval.rec = (PLpgSQL_rec *) (plpgsql_Datums[nse->itemno]);
return T_RECORD;
case PLPGSQL_NSTYPE_ROW: case PLPGSQL_NSTYPE_ROW:
plpgsql_yylval.row = (PLpgSQL_row *) (plpgsql_Datums[nse->itemno]); case PLPGSQL_NSTYPE_REC:
return T_ROW; plpgsql_yylval.datum = plpgsql_Datums[nse->itemno];
return T_DATUM;
default: default:
return T_ERROR; elog(ERROR, "unrecognized plpgsql itemtype: %d", nse->itemtype);
} }
} }
@ -1311,24 +1305,24 @@ plpgsql_parse_dblword(const char *word)
/* /*
* Do a lookup in the current namespace stack * Do a lookup in the current namespace stack
*/ */
ns = plpgsql_ns_lookup(plpgsql_ns_top(), ns = plpgsql_ns_lookup(plpgsql_ns_top(), false,
cp[0], cp[1], NULL, cp[0], cp[1], NULL,
&nnames); &nnames);
if (ns == NULL) if (ns == NULL)
{ {
pfree(cp[0]); pfree(cp[0]);
pfree(cp[1]); pfree(cp[1]);
return T_ERROR; return T_DBLWORD;
} }
switch (ns->itemtype) switch (ns->itemtype)
{ {
case PLPGSQL_NSTYPE_VAR: case PLPGSQL_NSTYPE_VAR:
/* Block-qualified reference to scalar variable. */ /* Block-qualified reference to scalar variable. */
plpgsql_yylval.scalar = plpgsql_Datums[ns->itemno]; plpgsql_yylval.datum = plpgsql_Datums[ns->itemno];
pfree(cp[0]); pfree(cp[0]);
pfree(cp[1]); pfree(cp[1]);
return T_SCALAR; return T_DATUM;
case PLPGSQL_NSTYPE_REC: case PLPGSQL_NSTYPE_REC:
if (nnames == 1) if (nnames == 1)
@ -1346,19 +1340,19 @@ plpgsql_parse_dblword(const char *word)
plpgsql_adddatum((PLpgSQL_datum *) new); plpgsql_adddatum((PLpgSQL_datum *) new);
plpgsql_yylval.scalar = (PLpgSQL_datum *) new; plpgsql_yylval.datum = (PLpgSQL_datum *) new;
pfree(cp[0]); pfree(cp[0]);
pfree(cp[1]); pfree(cp[1]);
return T_SCALAR; return T_DATUM;
} }
else else
{ {
/* Block-qualified reference to record variable. */ /* Block-qualified reference to record variable. */
plpgsql_yylval.rec = (PLpgSQL_rec *) (plpgsql_Datums[ns->itemno]); plpgsql_yylval.datum = plpgsql_Datums[ns->itemno];
pfree(cp[0]); pfree(cp[0]);
pfree(cp[1]); pfree(cp[1]);
return T_RECORD; return T_DATUM;
} }
case PLPGSQL_NSTYPE_ROW: case PLPGSQL_NSTYPE_ROW:
@ -1377,10 +1371,10 @@ plpgsql_parse_dblword(const char *word)
if (row->fieldnames[i] && if (row->fieldnames[i] &&
strcmp(row->fieldnames[i], cp[1]) == 0) strcmp(row->fieldnames[i], cp[1]) == 0)
{ {
plpgsql_yylval.scalar = plpgsql_Datums[row->varnos[i]]; plpgsql_yylval.datum = plpgsql_Datums[row->varnos[i]];
pfree(cp[0]); pfree(cp[0]);
pfree(cp[1]); pfree(cp[1]);
return T_SCALAR; return T_DATUM;
} }
} }
ereport(ERROR, ereport(ERROR,
@ -1391,10 +1385,10 @@ plpgsql_parse_dblword(const char *word)
else else
{ {
/* Block-qualified reference to row variable. */ /* Block-qualified reference to row variable. */
plpgsql_yylval.row = (PLpgSQL_row *) (plpgsql_Datums[ns->itemno]); plpgsql_yylval.datum = plpgsql_Datums[ns->itemno];
pfree(cp[0]); pfree(cp[0]);
pfree(cp[1]); pfree(cp[1]);
return T_ROW; return T_DATUM;
} }
default: default:
@ -1403,7 +1397,7 @@ plpgsql_parse_dblword(const char *word)
pfree(cp[0]); pfree(cp[0]);
pfree(cp[1]); pfree(cp[1]);
return T_ERROR; return T_DBLWORD;
} }
@ -1426,7 +1420,7 @@ plpgsql_parse_tripword(const char *word)
* Do a lookup in the current namespace stack. Must find a qualified * Do a lookup in the current namespace stack. Must find a qualified
* reference. * reference.
*/ */
ns = plpgsql_ns_lookup(plpgsql_ns_top(), ns = plpgsql_ns_lookup(plpgsql_ns_top(), false,
cp[0], cp[1], cp[2], cp[0], cp[1], cp[2],
&nnames); &nnames);
if (ns == NULL || nnames != 2) if (ns == NULL || nnames != 2)
@ -1434,7 +1428,7 @@ plpgsql_parse_tripword(const char *word)
pfree(cp[0]); pfree(cp[0]);
pfree(cp[1]); pfree(cp[1]);
pfree(cp[2]); pfree(cp[2]);
return T_ERROR; return T_TRIPWORD;
} }
switch (ns->itemtype) switch (ns->itemtype)
@ -1454,13 +1448,13 @@ plpgsql_parse_tripword(const char *word)
plpgsql_adddatum((PLpgSQL_datum *) new); plpgsql_adddatum((PLpgSQL_datum *) new);
plpgsql_yylval.scalar = (PLpgSQL_datum *) new; plpgsql_yylval.datum = (PLpgSQL_datum *) new;
pfree(cp[0]); pfree(cp[0]);
pfree(cp[1]); pfree(cp[1]);
pfree(cp[2]); pfree(cp[2]);
return T_SCALAR; return T_DATUM;
} }
case PLPGSQL_NSTYPE_ROW: case PLPGSQL_NSTYPE_ROW:
@ -1478,13 +1472,13 @@ plpgsql_parse_tripword(const char *word)
if (row->fieldnames[i] && if (row->fieldnames[i] &&
strcmp(row->fieldnames[i], cp[2]) == 0) strcmp(row->fieldnames[i], cp[2]) == 0)
{ {
plpgsql_yylval.scalar = plpgsql_Datums[row->varnos[i]]; plpgsql_yylval.datum = plpgsql_Datums[row->varnos[i]];
pfree(cp[0]); pfree(cp[0]);
pfree(cp[1]); pfree(cp[1]);
pfree(cp[2]); pfree(cp[2]);
return T_SCALAR; return T_DATUM;
} }
} }
ereport(ERROR, ereport(ERROR,
@ -1500,41 +1494,34 @@ plpgsql_parse_tripword(const char *word)
pfree(cp[0]); pfree(cp[0]);
pfree(cp[1]); pfree(cp[1]);
pfree(cp[2]); pfree(cp[2]);
return T_ERROR; return T_TRIPWORD;
} }
/* ---------- /* ----------
* plpgsql_parse_wordtype The scanner found word%TYPE. word can be * plpgsql_parse_wordtype The scanner found word%TYPE. word can be
* a variable name or a basetype. * a variable name or a basetype.
*
* Returns datatype struct, or NULL if no match found for word.
* ---------- * ----------
*/ */
int PLpgSQL_type *
plpgsql_parse_wordtype(char *word) plpgsql_parse_wordtype(const char *word)
{ {
PLpgSQL_type *dtype;
PLpgSQL_nsitem *nse; PLpgSQL_nsitem *nse;
bool old_nsstate;
HeapTuple typeTup; HeapTuple typeTup;
char *cp[2]; char *cp[1];
int i;
/* Do case conversion and word separation */ /* Do case conversion and word separation */
/* We convert %type to .type momentarily to keep converter happy */ plpgsql_convert_ident(word, cp, 1);
i = strlen(word) - 5;
Assert(word[i] == '%');
word[i] = '.';
plpgsql_convert_ident(word, cp, 2);
word[i] = '%';
pfree(cp[1]);
/* /*
* Do a lookup in the current namespace stack. Ensure we scan all levels. * Do a lookup in the current namespace stack
*/ */
old_nsstate = plpgsql_ns_setlocal(false); nse = plpgsql_ns_lookup(plpgsql_ns_top(), false,
nse = plpgsql_ns_lookup(plpgsql_ns_top(),
cp[0], NULL, NULL, cp[0], NULL, NULL,
NULL); NULL);
plpgsql_ns_setlocal(old_nsstate);
if (nse != NULL) if (nse != NULL)
{ {
@ -1542,13 +1529,12 @@ plpgsql_parse_wordtype(char *word)
switch (nse->itemtype) switch (nse->itemtype)
{ {
case PLPGSQL_NSTYPE_VAR: case PLPGSQL_NSTYPE_VAR:
plpgsql_yylval.dtype = ((PLpgSQL_var *) (plpgsql_Datums[nse->itemno]))->datatype; return ((PLpgSQL_var *) (plpgsql_Datums[nse->itemno]))->datatype;
return T_DTYPE;
/* XXX perhaps allow REC here? */ /* XXX perhaps allow REC/ROW here? */
default: default:
return T_ERROR; return NULL;
} }
} }
@ -1566,14 +1552,14 @@ plpgsql_parse_wordtype(char *word)
{ {
ReleaseSysCache(typeTup); ReleaseSysCache(typeTup);
pfree(cp[0]); pfree(cp[0]);
return T_ERROR; return NULL;
} }
plpgsql_yylval.dtype = build_datatype(typeTup, -1); dtype = build_datatype(typeTup, -1);
ReleaseSysCache(typeTup); ReleaseSysCache(typeTup);
pfree(cp[0]); pfree(cp[0]);
return T_DTYPE; return dtype;
} }
/* /*
@ -1581,7 +1567,7 @@ plpgsql_parse_wordtype(char *word)
* us. * us.
*/ */
pfree(cp[0]); pfree(cp[0]);
return T_ERROR; return NULL;
} }
@ -1589,49 +1575,38 @@ plpgsql_parse_wordtype(char *word)
* plpgsql_parse_dblwordtype Same lookup for word.word%TYPE * plpgsql_parse_dblwordtype Same lookup for word.word%TYPE
* ---------- * ----------
*/ */
int PLpgSQL_type *
plpgsql_parse_dblwordtype(char *word) plpgsql_parse_dblwordtype(const char *word)
{ {
PLpgSQL_type *dtype = NULL;
PLpgSQL_nsitem *nse; PLpgSQL_nsitem *nse;
bool old_nsstate;
Oid classOid; Oid classOid;
HeapTuple classtup = NULL; HeapTuple classtup = NULL;
HeapTuple attrtup = NULL; HeapTuple attrtup = NULL;
HeapTuple typetup = NULL; HeapTuple typetup = NULL;
Form_pg_class classStruct; Form_pg_class classStruct;
Form_pg_attribute attrStruct; Form_pg_attribute attrStruct;
char *cp[3]; char *cp[2];
int i;
MemoryContext oldCxt; MemoryContext oldCxt;
int result = T_ERROR;
/* Avoid memory leaks in the long-term function context */ /* Avoid memory leaks in the long-term function context */
oldCxt = MemoryContextSwitchTo(compile_tmp_cxt); oldCxt = MemoryContextSwitchTo(compile_tmp_cxt);
/* Do case conversion and word separation */ /* Do case conversion and word separation */
/* We convert %type to .type momentarily to keep converter happy */ plpgsql_convert_ident(word, cp, 2);
i = strlen(word) - 5;
Assert(word[i] == '%');
word[i] = '.';
plpgsql_convert_ident(word, cp, 3);
word[i] = '%';
pfree(cp[2]);
/* /*
* Do a lookup in the current namespace stack. Ensure we scan all levels. * Do a lookup in the current namespace stack.
* We don't need to check number of names matched, because we will only * We don't need to check number of names matched, because we will only
* consider scalar variables. * consider scalar variables.
*/ */
old_nsstate = plpgsql_ns_setlocal(false); nse = plpgsql_ns_lookup(plpgsql_ns_top(), false,
nse = plpgsql_ns_lookup(plpgsql_ns_top(),
cp[0], cp[1], NULL, cp[0], cp[1], NULL,
NULL); NULL);
plpgsql_ns_setlocal(old_nsstate);
if (nse != NULL && nse->itemtype == PLPGSQL_NSTYPE_VAR) if (nse != NULL && nse->itemtype == PLPGSQL_NSTYPE_VAR)
{ {
plpgsql_yylval.dtype = ((PLpgSQL_var *) (plpgsql_Datums[nse->itemno]))->datatype; dtype = ((PLpgSQL_var *) (plpgsql_Datums[nse->itemno]))->datatype;
result = T_DTYPE;
goto done; goto done;
} }
@ -1677,9 +1652,8 @@ plpgsql_parse_dblwordtype(char *word)
* return it * return it
*/ */
MemoryContextSwitchTo(oldCxt); MemoryContextSwitchTo(oldCxt);
plpgsql_yylval.dtype = build_datatype(typetup, attrStruct->atttypmod); dtype = build_datatype(typetup, attrStruct->atttypmod);
MemoryContextSwitchTo(compile_tmp_cxt); MemoryContextSwitchTo(compile_tmp_cxt);
result = T_DTYPE;
done: done:
if (HeapTupleIsValid(classtup)) if (HeapTupleIsValid(classtup))
@ -1690,39 +1664,32 @@ done:
ReleaseSysCache(typetup); ReleaseSysCache(typetup);
MemoryContextSwitchTo(oldCxt); MemoryContextSwitchTo(oldCxt);
return result; return dtype;
} }
/* ---------- /* ----------
* plpgsql_parse_tripwordtype Same lookup for word.word.word%TYPE * plpgsql_parse_tripwordtype Same lookup for word.word.word%TYPE
* ---------- * ----------
*/ */
int PLpgSQL_type *
plpgsql_parse_tripwordtype(char *word) plpgsql_parse_tripwordtype(const char *word)
{ {
PLpgSQL_type *dtype = NULL;
Oid classOid; Oid classOid;
HeapTuple classtup = NULL; HeapTuple classtup = NULL;
HeapTuple attrtup = NULL; HeapTuple attrtup = NULL;
HeapTuple typetup = NULL; HeapTuple typetup = NULL;
Form_pg_class classStruct; Form_pg_class classStruct;
Form_pg_attribute attrStruct; Form_pg_attribute attrStruct;
char *cp[4]; char *cp[3];
int i;
RangeVar *relvar; RangeVar *relvar;
MemoryContext oldCxt; MemoryContext oldCxt;
int result = T_ERROR;
/* Avoid memory leaks in the long-term function context */ /* Avoid memory leaks in the long-term function context */
oldCxt = MemoryContextSwitchTo(compile_tmp_cxt); oldCxt = MemoryContextSwitchTo(compile_tmp_cxt);
/* Do case conversion and word separation */ /* Do case conversion and word separation */
/* We convert %type to .type momentarily to keep converter happy */ plpgsql_convert_ident(word, cp, 3);
i = strlen(word) - 5;
Assert(word[i] == '%');
word[i] = '.';
plpgsql_convert_ident(word, cp, 4);
word[i] = '%';
pfree(cp[3]);
relvar = makeRangeVar(cp[0], cp[1], -1); relvar = makeRangeVar(cp[0], cp[1], -1);
classOid = RangeVarGetRelid(relvar, true); classOid = RangeVarGetRelid(relvar, true);
@ -1764,9 +1731,8 @@ plpgsql_parse_tripwordtype(char *word)
* return it * return it
*/ */
MemoryContextSwitchTo(oldCxt); MemoryContextSwitchTo(oldCxt);
plpgsql_yylval.dtype = build_datatype(typetup, attrStruct->atttypmod); dtype = build_datatype(typetup, attrStruct->atttypmod);
MemoryContextSwitchTo(compile_tmp_cxt); MemoryContextSwitchTo(compile_tmp_cxt);
result = T_DTYPE;
done: done:
if (HeapTupleIsValid(classtup)) if (HeapTupleIsValid(classtup))
@ -1777,7 +1743,7 @@ done:
ReleaseSysCache(typetup); ReleaseSysCache(typetup);
MemoryContextSwitchTo(oldCxt); MemoryContextSwitchTo(oldCxt);
return result; return dtype;
} }
/* ---------- /* ----------
@ -1785,20 +1751,15 @@ done:
* So word must be a table name. * So word must be a table name.
* ---------- * ----------
*/ */
int PLpgSQL_type *
plpgsql_parse_wordrowtype(char *word) plpgsql_parse_wordrowtype(const char *word)
{ {
PLpgSQL_type *dtype;
Oid classOid; Oid classOid;
char *cp[2]; char *cp[1];
int i;
/* Do case conversion and word separation */ /* Do case conversion and word separation */
/* We convert %rowtype to .rowtype momentarily to keep converter happy */ plpgsql_convert_ident(word, cp, 1);
i = strlen(word) - 8;
Assert(word[i] == '%');
word[i] = '.';
plpgsql_convert_ident(word, cp, 2);
word[i] = '%';
/* Lookup the relation */ /* Lookup the relation */
classOid = RelnameGetRelid(cp[0]); classOid = RelnameGetRelid(cp[0]);
@ -1807,16 +1768,12 @@ plpgsql_parse_wordrowtype(char *word)
(errcode(ERRCODE_UNDEFINED_TABLE), (errcode(ERRCODE_UNDEFINED_TABLE),
errmsg("relation \"%s\" does not exist", cp[0]))); errmsg("relation \"%s\" does not exist", cp[0])));
/* /* Build and return the row type struct */
* Build and return the row type struct dtype = plpgsql_build_datatype(get_rel_type_id(classOid), -1);
*/
plpgsql_yylval.dtype = plpgsql_build_datatype(get_rel_type_id(classOid),
-1);
pfree(cp[0]); pfree(cp[0]);
pfree(cp[1]);
return T_DTYPE; return dtype;
} }
/* ---------- /* ----------
@ -1824,12 +1781,12 @@ plpgsql_parse_wordrowtype(char *word)
* So word must be a namespace qualified table name. * So word must be a namespace qualified table name.
* ---------- * ----------
*/ */
int PLpgSQL_type *
plpgsql_parse_dblwordrowtype(char *word) plpgsql_parse_dblwordrowtype(const char *word)
{ {
PLpgSQL_type *dtype;
Oid classOid; Oid classOid;
char *cp[3]; char *cp[2];
int i;
RangeVar *relvar; RangeVar *relvar;
MemoryContext oldCxt; MemoryContext oldCxt;
@ -1837,12 +1794,7 @@ plpgsql_parse_dblwordrowtype(char *word)
oldCxt = MemoryContextSwitchTo(compile_tmp_cxt); oldCxt = MemoryContextSwitchTo(compile_tmp_cxt);
/* Do case conversion and word separation */ /* Do case conversion and word separation */
/* We convert %rowtype to .rowtype momentarily to keep converter happy */ plpgsql_convert_ident(word, cp, 2);
i = strlen(word) - 8;
Assert(word[i] == '%');
word[i] = '.';
plpgsql_convert_ident(word, cp, 3);
word[i] = '%';
/* Lookup the relation */ /* Lookup the relation */
relvar = makeRangeVar(cp[0], cp[1], -1); relvar = makeRangeVar(cp[0], cp[1], -1);
@ -1852,12 +1804,12 @@ plpgsql_parse_dblwordrowtype(char *word)
(errcode(ERRCODE_UNDEFINED_TABLE), (errcode(ERRCODE_UNDEFINED_TABLE),
errmsg("relation \"%s.%s\" does not exist", cp[0], cp[1]))); errmsg("relation \"%s.%s\" does not exist", cp[0], cp[1])));
/* Build and return the row type struct */
plpgsql_yylval.dtype = plpgsql_build_datatype(get_rel_type_id(classOid),
-1);
MemoryContextSwitchTo(oldCxt); MemoryContextSwitchTo(oldCxt);
return T_DTYPE;
/* Build and return the row type struct */
dtype = plpgsql_build_datatype(get_rel_type_id(classOid), -1);
return dtype;
} }
/* /*

View File

@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_funcs.c,v 1.84 2009/11/06 18:37:54 tgl Exp $ * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_funcs.c,v 1.85 2009/11/07 00:52:26 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -35,7 +35,6 @@
* ---------- * ----------
*/ */
static PLpgSQL_nsitem *ns_top = NULL; static PLpgSQL_nsitem *ns_top = NULL;
static bool ns_localmode = false;
/* ---------- /* ----------
@ -46,32 +45,6 @@ void
plpgsql_ns_init(void) plpgsql_ns_init(void)
{ {
ns_top = NULL; ns_top = NULL;
ns_localmode = false;
}
/* ----------
* plpgsql_ns_setlocal Tell plpgsql_ns_lookup whether to
* look into the current level only.
*
* This is a crock, but in the current design we need it because scan.l
* initiates name lookup, and the scanner does not know whether we are
* examining a name being declared in a DECLARE section. For that case
* we only want to know if there is a conflicting name earlier in the
* same DECLARE section. So the grammar must temporarily set local mode
* before scanning decl_varnames. This should eventually go away in favor
* of a localmode argument to plpgsql_ns_lookup, or perhaps some less
* indirect method of dealing with duplicate namespace entries.
* ----------
*/
bool
plpgsql_ns_setlocal(bool flag)
{
bool oldstate;
oldstate = ns_localmode;
ns_localmode = flag;
return oldstate;
} }
@ -140,6 +113,8 @@ plpgsql_ns_additem(int itemtype, int itemno, const char *name)
* *
* Note that this only searches for variables, not labels. * Note that this only searches for variables, not labels.
* *
* If localmode is TRUE, only the topmost block level is searched.
*
* name1 must be non-NULL. Pass NULL for name2 and/or name3 if parsing a name * name1 must be non-NULL. Pass NULL for name2 and/or name3 if parsing a name
* with fewer than three components. * with fewer than three components.
* *
@ -154,7 +129,7 @@ plpgsql_ns_additem(int itemtype, int itemno, const char *name)
* ---------- * ----------
*/ */
PLpgSQL_nsitem * PLpgSQL_nsitem *
plpgsql_ns_lookup(PLpgSQL_nsitem *ns_cur, plpgsql_ns_lookup(PLpgSQL_nsitem *ns_cur, bool localmode,
const char *name1, const char *name2, const char *name3, const char *name1, const char *name2, const char *name3,
int *names_used) int *names_used)
{ {
@ -201,7 +176,7 @@ plpgsql_ns_lookup(PLpgSQL_nsitem *ns_cur,
} }
} }
if (ns_localmode) if (localmode)
break; /* do not look into upper levels */ break; /* do not look into upper levels */
ns_cur = nsitem->prev; ns_cur = nsitem->prev;

View File

@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.120 2009/11/06 18:37:54 tgl Exp $ * $PostgreSQL: pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.121 2009/11/07 00:52:26 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -773,6 +773,7 @@ typedef struct
**********************************************************************/ **********************************************************************/
extern bool plpgsql_DumpExecTree; extern bool plpgsql_DumpExecTree;
extern bool plpgsql_LookupIdentifiers;
extern bool plpgsql_SpaceScanned; extern bool plpgsql_SpaceScanned;
extern int plpgsql_nDatums; extern int plpgsql_nDatums;
extern PLpgSQL_datum **plpgsql_Datums; extern PLpgSQL_datum **plpgsql_Datums;
@ -807,11 +808,11 @@ extern void plpgsql_parser_setup(struct ParseState *pstate,
extern int plpgsql_parse_word(const char *word); extern int plpgsql_parse_word(const char *word);
extern int plpgsql_parse_dblword(const char *word); extern int plpgsql_parse_dblword(const char *word);
extern int plpgsql_parse_tripword(const char *word); extern int plpgsql_parse_tripword(const char *word);
extern int plpgsql_parse_wordtype(char *word); extern PLpgSQL_type *plpgsql_parse_wordtype(const char *word);
extern int plpgsql_parse_dblwordtype(char *word); extern PLpgSQL_type *plpgsql_parse_dblwordtype(const char *word);
extern int plpgsql_parse_tripwordtype(char *word); extern PLpgSQL_type *plpgsql_parse_tripwordtype(const char *word);
extern int plpgsql_parse_wordrowtype(char *word); extern PLpgSQL_type *plpgsql_parse_wordrowtype(const char *word);
extern int plpgsql_parse_dblwordrowtype(char *word); extern PLpgSQL_type *plpgsql_parse_dblwordrowtype(const char *word);
extern PLpgSQL_type *plpgsql_parse_datatype(const char *string); extern PLpgSQL_type *plpgsql_parse_datatype(const char *string);
extern PLpgSQL_type *plpgsql_build_datatype(Oid typeOid, int32 typmod); extern PLpgSQL_type *plpgsql_build_datatype(Oid typeOid, int32 typmod);
extern PLpgSQL_variable *plpgsql_build_variable(const char *refname, int lineno, extern PLpgSQL_variable *plpgsql_build_variable(const char *refname, int lineno,
@ -857,12 +858,11 @@ extern Oid exec_get_rec_fieldtype(PLpgSQL_rec *rec, const char *fieldname,
* ---------- * ----------
*/ */
extern void plpgsql_ns_init(void); extern void plpgsql_ns_init(void);
extern bool plpgsql_ns_setlocal(bool flag);
extern void plpgsql_ns_push(const char *label); extern void plpgsql_ns_push(const char *label);
extern void plpgsql_ns_pop(void); extern void plpgsql_ns_pop(void);
extern PLpgSQL_nsitem *plpgsql_ns_top(void); extern PLpgSQL_nsitem *plpgsql_ns_top(void);
extern void plpgsql_ns_additem(int itemtype, int itemno, const char *name); extern void plpgsql_ns_additem(int itemtype, int itemno, const char *name);
extern PLpgSQL_nsitem *plpgsql_ns_lookup(PLpgSQL_nsitem *ns_cur, extern PLpgSQL_nsitem *plpgsql_ns_lookup(PLpgSQL_nsitem *ns_cur, bool localmode,
const char *name1, const char *name2, const char *name1, const char *name2,
const char *name3, int *names_used); const char *name3, int *names_used);
extern PLpgSQL_nsitem *plpgsql_ns_lookup_label(PLpgSQL_nsitem *ns_cur, extern PLpgSQL_nsitem *plpgsql_ns_lookup_label(PLpgSQL_nsitem *ns_cur,

View File

@ -9,7 +9,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/pl/plpgsql/src/scan.l,v 1.73 2009/11/05 16:58:36 tgl Exp $ * $PostgreSQL: pgsql/src/pl/plpgsql/src/scan.l,v 1.74 2009/11/07 00:52:26 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -43,6 +43,7 @@ static int cur_line_num;
static int xcdepth = 0; /* depth of nesting in slash-star comments */ static int xcdepth = 0; /* depth of nesting in slash-star comments */
static char *dolqstart; /* current $foo$ quote start string */ static char *dolqstart; /* current $foo$ quote start string */
bool plpgsql_LookupIdentifiers = true;
bool plpgsql_SpaceScanned = false; bool plpgsql_SpaceScanned = false;
%} %}
@ -209,52 +210,28 @@ dump { return O_DUMP; }
*/ */
{identifier} { {identifier} {
plpgsql_error_lineno = plpgsql_scanner_lineno(); plpgsql_error_lineno = plpgsql_scanner_lineno();
if (!plpgsql_LookupIdentifiers) return T_WORD;
return plpgsql_parse_word(yytext); } return plpgsql_parse_word(yytext); }
{identifier}{space}*\.{space}*{identifier} { {identifier}{space}*\.{space}*{identifier} {
plpgsql_error_lineno = plpgsql_scanner_lineno(); plpgsql_error_lineno = plpgsql_scanner_lineno();
if (!plpgsql_LookupIdentifiers) return T_DBLWORD;
return plpgsql_parse_dblword(yytext); } return plpgsql_parse_dblword(yytext); }
{identifier}{space}*\.{space}*{identifier}{space}*\.{space}*{identifier} { {identifier}{space}*\.{space}*{identifier}{space}*\.{space}*{identifier} {
plpgsql_error_lineno = plpgsql_scanner_lineno(); plpgsql_error_lineno = plpgsql_scanner_lineno();
if (!plpgsql_LookupIdentifiers) return T_TRIPWORD;
return plpgsql_parse_tripword(yytext); } return plpgsql_parse_tripword(yytext); }
{identifier}{space}*%TYPE {
plpgsql_error_lineno = plpgsql_scanner_lineno();
return plpgsql_parse_wordtype(yytext); }
{identifier}{space}*\.{space}*{identifier}{space}*%TYPE {
plpgsql_error_lineno = plpgsql_scanner_lineno();
return plpgsql_parse_dblwordtype(yytext); }
{identifier}{space}*\.{space}*{identifier}{space}*\.{space}*{identifier}{space}*%TYPE {
plpgsql_error_lineno = plpgsql_scanner_lineno();
return plpgsql_parse_tripwordtype(yytext); }
{identifier}{space}*%ROWTYPE {
plpgsql_error_lineno = plpgsql_scanner_lineno();
return plpgsql_parse_wordrowtype(yytext); }
{identifier}{space}*\.{space}*{identifier}{space}*%ROWTYPE {
plpgsql_error_lineno = plpgsql_scanner_lineno();
return plpgsql_parse_dblwordrowtype(yytext); }
{param} { {param} {
plpgsql_error_lineno = plpgsql_scanner_lineno(); plpgsql_error_lineno = plpgsql_scanner_lineno();
if (!plpgsql_LookupIdentifiers) return T_WORD;
return plpgsql_parse_word(yytext); } return plpgsql_parse_word(yytext); }
{param}{space}*\.{space}*{identifier} { {param}{space}*\.{space}*{identifier} {
plpgsql_error_lineno = plpgsql_scanner_lineno(); plpgsql_error_lineno = plpgsql_scanner_lineno();
if (!plpgsql_LookupIdentifiers) return T_DBLWORD;
return plpgsql_parse_dblword(yytext); } return plpgsql_parse_dblword(yytext); }
{param}{space}*\.{space}*{identifier}{space}*\.{space}*{identifier} { {param}{space}*\.{space}*{identifier}{space}*\.{space}*{identifier} {
plpgsql_error_lineno = plpgsql_scanner_lineno(); plpgsql_error_lineno = plpgsql_scanner_lineno();
if (!plpgsql_LookupIdentifiers) return T_TRIPWORD;
return plpgsql_parse_tripword(yytext); } return plpgsql_parse_tripword(yytext); }
{param}{space}*%TYPE {
plpgsql_error_lineno = plpgsql_scanner_lineno();
return plpgsql_parse_wordtype(yytext); }
{param}{space}*\.{space}*{identifier}{space}*%TYPE {
plpgsql_error_lineno = plpgsql_scanner_lineno();
return plpgsql_parse_dblwordtype(yytext); }
{param}{space}*\.{space}*{identifier}{space}*\.{space}*{identifier}{space}*%TYPE {
plpgsql_error_lineno = plpgsql_scanner_lineno();
return plpgsql_parse_tripwordtype(yytext); }
{param}{space}*%ROWTYPE {
plpgsql_error_lineno = plpgsql_scanner_lineno();
return plpgsql_parse_wordrowtype(yytext); }
{param}{space}*\.{space}*{identifier}{space}*%ROWTYPE {
plpgsql_error_lineno = plpgsql_scanner_lineno();
return plpgsql_parse_dblwordrowtype(yytext); }
{digit}+ { return T_NUMBER; } {digit}+ { return T_NUMBER; }
@ -527,6 +504,8 @@ plpgsql_scanner_init(const char *str)
cur_line_start++; cur_line_start++;
BEGIN(INITIAL); BEGIN(INITIAL);
plpgsql_LookupIdentifiers = true;
plpgsql_SpaceScanned = false;
} }
/* /*