diff --git a/src/pl/plpgsql/src/gram.y b/src/pl/plpgsql/src/gram.y index c8fc92743c..bda7bb2b58 100644 --- a/src/pl/plpgsql/src/gram.y +++ b/src/pl/plpgsql/src/gram.y @@ -9,7 +9,7 @@ * * * 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; List *list; PLpgSQL_type *dtype; - PLpgSQL_datum *scalar; /* a VAR or RECFIELD */ - PLpgSQL_variable *variable; /* a VAR, REC, or ROW */ + PLpgSQL_datum *datum; PLpgSQL_var *var; - PLpgSQL_row *row; - PLpgSQL_rec *rec; PLpgSQL_expr *expr; PLpgSQL_stmt *stmt; PLpgSQL_stmt_block *program; @@ -131,7 +128,7 @@ static List *read_raise_options(void); %type decl_const decl_notnull exit_type %type decl_defval decl_cursor_query %type decl_datatype -%type decl_cursor_args +%type decl_cursor_args %type decl_cursor_arglist %type decl_aliasitem %type decl_stmts decl_stmt @@ -142,7 +139,7 @@ static List *read_raise_options(void); %type assign_var %type cursor_variable -%type decl_cursor_arg +%type decl_cursor_arg %type for_variable %type for_control @@ -234,12 +231,10 @@ static List *read_raise_options(void); */ %token T_STRING %token T_NUMBER -%token T_SCALAR /* a VAR or RECFIELD */ -%token T_ROW -%token T_RECORD -%token T_DTYPE -%token T_WORD -%token T_ERROR +%token T_DATUM /* a VAR, ROW, REC, or RECFIELD variable */ +%token T_WORD /* unrecognized simple identifier */ +%token T_DBLWORD /* unrecognized ident.ident */ +%token T_TRIPWORD /* unrecognized ident.ident.ident */ %token O_OPTION %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 { - plpgsql_ns_setlocal(false); + /* done with decls, so resume identifier lookup */ + plpgsql_LookupIdentifiers = true; $$.label = $1; $$.n_initvars = 0; $$.initvarnos = NULL; } | opt_block_label decl_start { - plpgsql_ns_setlocal(false); + plpgsql_LookupIdentifiers = true; $$.label = $1; $$.n_initvars = 0; $$.initvarnos = NULL; } | opt_block_label decl_start decl_stmts { - plpgsql_ns_setlocal(false); + plpgsql_LookupIdentifiers = true; if ($3 != NULL) $$.label = $3; else @@ -322,8 +318,11 @@ decl_start : K_DECLARE { /* Forget any variables created before block */ 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 : { - PLpgSQL_expr *query; - - plpgsql_ns_setlocal(false); - query = read_sql_stmt(""); - plpgsql_ns_setlocal(true); - - $$ = query; + $$ = read_sql_stmt(""); } ; @@ -486,7 +479,7 @@ decl_cursor_args : list_free($2); 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 { - $$ = plpgsql_build_variable($1.name, $1.lineno, - $2, true); + $$ = (PLpgSQL_datum *) + plpgsql_build_variable($1.name, $1.lineno, + $2, true); } ; decl_is_for : K_IS | /* Oracle */ K_FOR; /* SQL standard */ -decl_aliasitem : any_identifier +decl_aliasitem : T_WORD { - char *name; + char *name[1]; PLpgSQL_nsitem *nsi; - /* XXX should allow block-label-qualified names */ - plpgsql_convert_ident($1, &name, 1); + plpgsql_convert_ident(yytext, name, 1); - plpgsql_ns_setlocal(false); - - nsi = plpgsql_ns_lookup(plpgsql_ns_top(), - name, NULL, NULL, + nsi = plpgsql_ns_lookup(plpgsql_ns_top(), false, + name[0], NULL, NULL, NULL); if (nsi == NULL) { @@ -529,12 +520,34 @@ decl_aliasitem : any_identifier ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), 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; } @@ -547,24 +560,14 @@ decl_varname : T_WORD plpgsql_convert_ident(yytext, &name, 1); $$.name = name; $$.lineno = plpgsql_scanner_lineno(); - } - | T_SCALAR - { /* - * Since the scanner is only searching the topmost - * namespace level, getting T_SCALAR etc can only - * happen if the name is already declared in this - * block. + * Check to make sure name isn't already declared + * in the current block. */ - yyerror("duplicate declaration"); - } - | T_ROW - { - yyerror("duplicate declaration"); - } - | T_RECORD - { - yyerror("duplicate declaration"); + if (plpgsql_ns_lookup(plpgsql_ns_top(), true, + name, NULL, NULL, + NULL) != NULL) + yyerror("duplicate declaration"); } ; @@ -595,9 +598,7 @@ decl_defval : ';' { $$ = NULL; } | decl_defkey { - plpgsql_ns_setlocal(false); $$ = 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); - $$ = yylval.scalar->dno; - } - | T_ROW - { - yyerror("expected an integer variable"); - } - | T_RECORD - { - yyerror("expected an integer variable"); + 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))); + $$ = yylval.datum->dno; } | 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); - $$ = yylval.scalar->dno; - } - | T_ROW - { - check_assignable((PLpgSQL_datum *) yylval.row); - $$ = yylval.row->dno; - } - | T_RECORD - { - check_assignable((PLpgSQL_datum *) yylval.rec); - $$ = yylval.rec->dno; + check_assignable(yylval.datum); + $$ = yylval.datum->dno; } | assign_var '[' expr_until_rightbracket { @@ -783,7 +776,7 @@ assign_var : T_SCALAR new->subscript = $3; new->arrayparentno = $1; - plpgsql_adddatum((PLpgSQL_datum *)new); + plpgsql_adddatum((PLpgSQL_datum *) new); $$ = new->dno; } @@ -1026,13 +1019,13 @@ for_control : $$ = (PLpgSQL_stmt *) new; } - else if (tok == T_SCALAR && - yylval.scalar->dtype == PLPGSQL_DTYPE_VAR && - ((PLpgSQL_var *) yylval.scalar)->datatype->typoid == REFCURSOROID) + else if (tok == T_DATUM && + yylval.datum->dtype == PLPGSQL_DTYPE_VAR && + ((PLpgSQL_var *) yylval.datum)->datatype->typoid == REFCURSOROID) { /* It's FOR var IN cursor */ PLpgSQL_stmt_forc *new; - PLpgSQL_var *cursor = (PLpgSQL_var *) yylval.scalar; + PLpgSQL_var *cursor = (PLpgSQL_var *) yylval.datum; char *varname; 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 * $$.row; see the for_control production. */ -for_variable : T_SCALAR +for_variable : T_DATUM { - int tok; - $$.name = pstrdup(yytext); $$.lineno = plpgsql_scanner_lineno(); - $$.scalar = yylval.scalar; - $$.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); + if (yylval.datum->dtype == PLPGSQL_DTYPE_ROW) + { + $$.scalar = NULL; + $$.rec = NULL; + $$.row = (PLpgSQL_row *) yylval.datum; + } + else if (yylval.datum->dtype == PLPGSQL_DTYPE_REC) + { + $$.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 { @@ -1259,26 +1268,10 @@ for_variable : T_SCALAR plpgsql_error_lineno = $$.lineno; ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("\"%s\" is not a scalar variable", + errmsg("\"%s\" is not a known variable", $$.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 @@ -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 { $$ = pstrdup(yytext); } | T_WORD { $$ = pstrdup(yytext); } - | T_ERROR + | T_DBLWORD + { $$ = pstrdup(yytext); } + | T_TRIPWORD { $$ = 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"); - if (((PLpgSQL_var *) yylval.scalar)->datatype->typoid != REFCURSOROID) + if (((PLpgSQL_var *) yylval.datum)->datatype->typoid != REFCURSOROID) { plpgsql_error_lineno = plpgsql_scanner_lineno(); ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("variable \"%s\" must be of type cursor or refcursor", - ((PLpgSQL_var *) yylval.scalar)->refname))); + ((PLpgSQL_var *) yylval.datum)->refname))); } - $$ = (PLpgSQL_var *) yylval.scalar; - } - | T_ROW - { - yyerror("expected a cursor or refcursor variable"); - } - | T_RECORD - { - yyerror("expected a cursor or refcursor variable"); + $$ = (PLpgSQL_var *) yylval.datum; } | 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 { $$ = yytext; } - | T_SCALAR - { - $$ = yytext; - } - | T_RECORD - { - $$ = yytext; - } - | T_ROW + | T_DATUM { $$ = yytext; } @@ -1941,6 +1928,7 @@ read_sql_construct(int until, int tok; int lno; StringInfoData ds; + bool save_LookupIdentifiers; int parenlevel = 0; PLpgSQL_expr *expr; @@ -1948,6 +1936,10 @@ read_sql_construct(int until, initStringInfo(&ds); appendStringInfoString(&ds, sqlstart); + /* no need to lookup identifiers within the SQL text */ + save_LookupIdentifiers = plpgsql_LookupIdentifiers; + plpgsql_LookupIdentifiers = false; + for (;;) { tok = yylex(); @@ -1992,6 +1984,8 @@ read_sql_construct(int until, appendStringInfoString(&ds, yytext); } + plpgsql_LookupIdentifiers = save_LookupIdentifiers; + if (endtoken) *endtoken = tok; @@ -2019,19 +2013,100 @@ read_datatype(int tok) bool needspace = false; int parenlevel = 0; + /* Should always be called with LookupIdentifiers off */ + Assert(!plpgsql_LookupIdentifiers); + lno = plpgsql_scanner_lineno(); + initStringInfo(&ds); + /* Often there will be a lookahead token, but if not, get one */ if (tok == YYEMPTY) 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 */ - return yylval.dtype; + appendStringInfoString(&ds, yytext); + 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 != ';') { @@ -2080,6 +2155,7 @@ static PLpgSQL_stmt * make_execsql_stmt(const char *sqlstart, int lineno) { StringInfoData ds; + bool save_LookupIdentifiers; PLpgSQL_stmt_execsql *execsql; PLpgSQL_expr *expr; PLpgSQL_row *row = NULL; @@ -2092,6 +2168,10 @@ make_execsql_stmt(const char *sqlstart, int lineno) initStringInfo(&ds); 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 * 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) yyerror("INTO specified more than once"); have_into = true; + plpgsql_LookupIdentifiers = true; read_into_target(&rec, &row, &have_strict); + plpgsql_LookupIdentifiers = false; continue; } @@ -2128,6 +2210,8 @@ make_execsql_stmt(const char *sqlstart, int lineno) appendStringInfoString(&ds, yytext); } + plpgsql_LookupIdentifiers = save_LookupIdentifiers; + expr = palloc0(sizeof(PLpgSQL_expr)); expr->dtype = PLPGSQL_DTYPE_EXPR; expr->query = pstrdup(ds.data); @@ -2233,7 +2317,7 @@ read_fetch_direction(void) /* empty direction */ 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 */ plpgsql_push_back_token(tok); @@ -2245,7 +2329,7 @@ read_fetch_direction(void) * Assume it's a count expression with no preceding keyword. * Note: we allow this syntax because core SQL does, but we don't * 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 * lot of work. */ @@ -2342,12 +2426,12 @@ make_return_stmt(int lineno) /* we allow this to support RETURN NULL in triggers */ break; - case T_ROW: - new->retvarno = yylval.row->dno; - break; - - case T_RECORD: - new->retvarno = yylval.rec->dno; + case T_DATUM: + if (yylval.datum->dtype == PLPGSQL_DTYPE_ROW || + yylval.datum->dtype == PLPGSQL_DTYPE_REC) + new->retvarno = yylval.datum->dno; + else + yyerror("RETURN must specify a record or row variable in function returning row"); break; default: @@ -2395,12 +2479,12 @@ make_return_next_stmt(int lineno) { switch (yylex()) { - case T_ROW: - new->retvarno = yylval.row->dno; - break; - - case T_RECORD: - new->retvarno = yylval.rec->dno; + case T_DATUM: + if (yylval.datum->dtype == PLPGSQL_DTYPE_ROW || + yylval.datum->dtype == PLPGSQL_DTYPE_REC) + new->retvarno = yylval.datum->dno; + else + yyerror("RETURN NEXT must specify a record or row variable in function returning row"); break; default: @@ -2517,18 +2601,21 @@ read_into_target(PLpgSQL_rec **rec, PLpgSQL_row **row, bool *strict) switch (tok) { - case T_ROW: - *row = yylval.row; - check_assignable((PLpgSQL_datum *) *row); - break; - - case T_RECORD: - *rec = yylval.rec; - check_assignable((PLpgSQL_datum *) *rec); - break; - - case T_SCALAR: - *row = read_into_scalar_list(yytext, yylval.scalar); + case T_DATUM: + if (yylval.datum->dtype == PLPGSQL_DTYPE_ROW) + { + check_assignable(yylval.datum); + *row = (PLpgSQL_row *) yylval.datum; + } + else if (yylval.datum->dtype == PLPGSQL_DTYPE_REC) + { + check_assignable(yylval.datum); + *rec = (PLpgSQL_rec *) yylval.datum; + } + else + { + *row = read_into_scalar_list(yytext, yylval.datum); + } break; default: @@ -2576,17 +2663,23 @@ read_into_scalar_list(const char *initial_name, tok = yylex(); switch(tok) { - case T_SCALAR: - check_assignable(yylval.scalar); + case T_DATUM: + 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); - varnos[nfields++] = yylval.scalar->dno; + varnos[nfields++] = yylval.datum->dno; break; default: plpgsql_error_lineno = plpgsql_scanner_lineno(); ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("\"%s\" is not a scalar variable", + errmsg("\"%s\" is not a known variable", yytext))); } } diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c index be1d90a2f2..4c232673b3 100644 --- a/src/pl/plpgsql/src/pl_comp.c +++ b/src/pl/plpgsql/src/pl_comp.c @@ -8,7 +8,7 @@ * * * 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); - nse = plpgsql_ns_lookup(expr->ns, + nse = plpgsql_ns_lookup(expr->ns, false, pname, NULL, NULL, NULL); @@ -1120,7 +1120,7 @@ resolve_column_ref(PLpgSQL_expr *expr, ColumnRef *cref) return NULL; } - nse = plpgsql_ns_lookup(expr->ns, + nse = plpgsql_ns_lookup(expr->ns, false, name1, name2, name3, &nnames); @@ -1200,7 +1200,7 @@ resolve_column_ref(PLpgSQL_expr *expr, ColumnRef *cref) } break; default: - elog(ERROR, "unrecognized plpgsql itemtype"); + elog(ERROR, "unrecognized plpgsql itemtype: %d", nse->itemtype); } /* 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 */ - nse = plpgsql_ns_lookup(plpgsql_ns_top(), + nse = plpgsql_ns_lookup(plpgsql_ns_top(), false, cp[0], NULL, NULL, NULL); pfree(cp[0]); @@ -1269,19 +1269,13 @@ plpgsql_parse_word(const char *word) switch (nse->itemtype) { 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: - plpgsql_yylval.row = (PLpgSQL_row *) (plpgsql_Datums[nse->itemno]); - return T_ROW; + case PLPGSQL_NSTYPE_REC: + plpgsql_yylval.datum = plpgsql_Datums[nse->itemno]; + return T_DATUM; 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 */ - ns = plpgsql_ns_lookup(plpgsql_ns_top(), + ns = plpgsql_ns_lookup(plpgsql_ns_top(), false, cp[0], cp[1], NULL, &nnames); if (ns == NULL) { pfree(cp[0]); pfree(cp[1]); - return T_ERROR; + return T_DBLWORD; } switch (ns->itemtype) { case PLPGSQL_NSTYPE_VAR: /* 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[1]); - return T_SCALAR; + return T_DATUM; case PLPGSQL_NSTYPE_REC: if (nnames == 1) @@ -1346,19 +1340,19 @@ plpgsql_parse_dblword(const char *word) plpgsql_adddatum((PLpgSQL_datum *) new); - plpgsql_yylval.scalar = (PLpgSQL_datum *) new; + plpgsql_yylval.datum = (PLpgSQL_datum *) new; pfree(cp[0]); pfree(cp[1]); - return T_SCALAR; + return T_DATUM; } else { /* 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[1]); - return T_RECORD; + return T_DATUM; } case PLPGSQL_NSTYPE_ROW: @@ -1377,10 +1371,10 @@ plpgsql_parse_dblword(const char *word) if (row->fieldnames[i] && 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[1]); - return T_SCALAR; + return T_DATUM; } } ereport(ERROR, @@ -1391,10 +1385,10 @@ plpgsql_parse_dblword(const char *word) else { /* 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[1]); - return T_ROW; + return T_DATUM; } default: @@ -1403,7 +1397,7 @@ plpgsql_parse_dblword(const char *word) pfree(cp[0]); 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 * reference. */ - ns = plpgsql_ns_lookup(plpgsql_ns_top(), + ns = plpgsql_ns_lookup(plpgsql_ns_top(), false, cp[0], cp[1], cp[2], &nnames); if (ns == NULL || nnames != 2) @@ -1434,7 +1428,7 @@ plpgsql_parse_tripword(const char *word) pfree(cp[0]); pfree(cp[1]); pfree(cp[2]); - return T_ERROR; + return T_TRIPWORD; } switch (ns->itemtype) @@ -1454,13 +1448,13 @@ plpgsql_parse_tripword(const char *word) plpgsql_adddatum((PLpgSQL_datum *) new); - plpgsql_yylval.scalar = (PLpgSQL_datum *) new; + plpgsql_yylval.datum = (PLpgSQL_datum *) new; pfree(cp[0]); pfree(cp[1]); pfree(cp[2]); - return T_SCALAR; + return T_DATUM; } case PLPGSQL_NSTYPE_ROW: @@ -1478,13 +1472,13 @@ plpgsql_parse_tripword(const char *word) if (row->fieldnames[i] && 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[1]); pfree(cp[2]); - return T_SCALAR; + return T_DATUM; } } ereport(ERROR, @@ -1500,41 +1494,34 @@ plpgsql_parse_tripword(const char *word) pfree(cp[0]); pfree(cp[1]); pfree(cp[2]); - return T_ERROR; + return T_TRIPWORD; } /* ---------- * plpgsql_parse_wordtype The scanner found word%TYPE. word can be * a variable name or a basetype. + * + * Returns datatype struct, or NULL if no match found for word. * ---------- */ -int -plpgsql_parse_wordtype(char *word) +PLpgSQL_type * +plpgsql_parse_wordtype(const char *word) { + PLpgSQL_type *dtype; PLpgSQL_nsitem *nse; - bool old_nsstate; HeapTuple typeTup; - char *cp[2]; - int i; + char *cp[1]; /* Do case conversion and word separation */ - /* We convert %type to .type momentarily to keep converter happy */ - i = strlen(word) - 5; - Assert(word[i] == '%'); - word[i] = '.'; - plpgsql_convert_ident(word, cp, 2); - word[i] = '%'; - pfree(cp[1]); + plpgsql_convert_ident(word, 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(), + nse = plpgsql_ns_lookup(plpgsql_ns_top(), false, cp[0], NULL, NULL, NULL); - plpgsql_ns_setlocal(old_nsstate); if (nse != NULL) { @@ -1542,13 +1529,12 @@ plpgsql_parse_wordtype(char *word) switch (nse->itemtype) { case PLPGSQL_NSTYPE_VAR: - plpgsql_yylval.dtype = ((PLpgSQL_var *) (plpgsql_Datums[nse->itemno]))->datatype; - return T_DTYPE; + return ((PLpgSQL_var *) (plpgsql_Datums[nse->itemno]))->datatype; - /* XXX perhaps allow REC here? */ + /* XXX perhaps allow REC/ROW here? */ default: - return T_ERROR; + return NULL; } } @@ -1566,14 +1552,14 @@ plpgsql_parse_wordtype(char *word) { ReleaseSysCache(typeTup); pfree(cp[0]); - return T_ERROR; + return NULL; } - plpgsql_yylval.dtype = build_datatype(typeTup, -1); + dtype = build_datatype(typeTup, -1); ReleaseSysCache(typeTup); pfree(cp[0]); - return T_DTYPE; + return dtype; } /* @@ -1581,7 +1567,7 @@ plpgsql_parse_wordtype(char *word) * us. */ 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 * ---------- */ -int -plpgsql_parse_dblwordtype(char *word) +PLpgSQL_type * +plpgsql_parse_dblwordtype(const char *word) { + PLpgSQL_type *dtype = NULL; PLpgSQL_nsitem *nse; - bool old_nsstate; Oid classOid; HeapTuple classtup = NULL; HeapTuple attrtup = NULL; HeapTuple typetup = NULL; Form_pg_class classStruct; Form_pg_attribute attrStruct; - char *cp[3]; - int i; + char *cp[2]; MemoryContext oldCxt; - int result = T_ERROR; /* Avoid memory leaks in the long-term function context */ oldCxt = MemoryContextSwitchTo(compile_tmp_cxt); /* Do case conversion and word separation */ - /* We convert %type to .type momentarily to keep converter happy */ - i = strlen(word) - 5; - Assert(word[i] == '%'); - word[i] = '.'; - plpgsql_convert_ident(word, cp, 3); - word[i] = '%'; - pfree(cp[2]); + plpgsql_convert_ident(word, 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 * consider scalar variables. */ - old_nsstate = plpgsql_ns_setlocal(false); - nse = plpgsql_ns_lookup(plpgsql_ns_top(), + nse = plpgsql_ns_lookup(plpgsql_ns_top(), false, cp[0], cp[1], NULL, NULL); - plpgsql_ns_setlocal(old_nsstate); if (nse != NULL && nse->itemtype == PLPGSQL_NSTYPE_VAR) { - plpgsql_yylval.dtype = ((PLpgSQL_var *) (plpgsql_Datums[nse->itemno]))->datatype; - result = T_DTYPE; + dtype = ((PLpgSQL_var *) (plpgsql_Datums[nse->itemno]))->datatype; goto done; } @@ -1677,9 +1652,8 @@ plpgsql_parse_dblwordtype(char *word) * return it */ MemoryContextSwitchTo(oldCxt); - plpgsql_yylval.dtype = build_datatype(typetup, attrStruct->atttypmod); + dtype = build_datatype(typetup, attrStruct->atttypmod); MemoryContextSwitchTo(compile_tmp_cxt); - result = T_DTYPE; done: if (HeapTupleIsValid(classtup)) @@ -1690,39 +1664,32 @@ done: ReleaseSysCache(typetup); MemoryContextSwitchTo(oldCxt); - return result; + return dtype; } /* ---------- * plpgsql_parse_tripwordtype Same lookup for word.word.word%TYPE * ---------- */ -int -plpgsql_parse_tripwordtype(char *word) +PLpgSQL_type * +plpgsql_parse_tripwordtype(const char *word) { + PLpgSQL_type *dtype = NULL; Oid classOid; HeapTuple classtup = NULL; HeapTuple attrtup = NULL; HeapTuple typetup = NULL; Form_pg_class classStruct; Form_pg_attribute attrStruct; - char *cp[4]; - int i; + char *cp[3]; RangeVar *relvar; MemoryContext oldCxt; - int result = T_ERROR; /* Avoid memory leaks in the long-term function context */ oldCxt = MemoryContextSwitchTo(compile_tmp_cxt); /* Do case conversion and word separation */ - /* We convert %type to .type momentarily to keep converter happy */ - i = strlen(word) - 5; - Assert(word[i] == '%'); - word[i] = '.'; - plpgsql_convert_ident(word, cp, 4); - word[i] = '%'; - pfree(cp[3]); + plpgsql_convert_ident(word, cp, 3); relvar = makeRangeVar(cp[0], cp[1], -1); classOid = RangeVarGetRelid(relvar, true); @@ -1764,9 +1731,8 @@ plpgsql_parse_tripwordtype(char *word) * return it */ MemoryContextSwitchTo(oldCxt); - plpgsql_yylval.dtype = build_datatype(typetup, attrStruct->atttypmod); + dtype = build_datatype(typetup, attrStruct->atttypmod); MemoryContextSwitchTo(compile_tmp_cxt); - result = T_DTYPE; done: if (HeapTupleIsValid(classtup)) @@ -1777,7 +1743,7 @@ done: ReleaseSysCache(typetup); MemoryContextSwitchTo(oldCxt); - return result; + return dtype; } /* ---------- @@ -1785,20 +1751,15 @@ done: * So word must be a table name. * ---------- */ -int -plpgsql_parse_wordrowtype(char *word) +PLpgSQL_type * +plpgsql_parse_wordrowtype(const char *word) { + PLpgSQL_type *dtype; Oid classOid; - char *cp[2]; - int i; + char *cp[1]; /* Do case conversion and word separation */ - /* We convert %rowtype to .rowtype momentarily to keep converter happy */ - i = strlen(word) - 8; - Assert(word[i] == '%'); - word[i] = '.'; - plpgsql_convert_ident(word, cp, 2); - word[i] = '%'; + plpgsql_convert_ident(word, cp, 1); /* Lookup the relation */ classOid = RelnameGetRelid(cp[0]); @@ -1807,16 +1768,12 @@ plpgsql_parse_wordrowtype(char *word) (errcode(ERRCODE_UNDEFINED_TABLE), errmsg("relation \"%s\" does not exist", cp[0]))); - /* - * Build and return the row type struct - */ - plpgsql_yylval.dtype = plpgsql_build_datatype(get_rel_type_id(classOid), - -1); + /* Build and return the row type struct */ + dtype = plpgsql_build_datatype(get_rel_type_id(classOid), -1); 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. * ---------- */ -int -plpgsql_parse_dblwordrowtype(char *word) +PLpgSQL_type * +plpgsql_parse_dblwordrowtype(const char *word) { + PLpgSQL_type *dtype; Oid classOid; - char *cp[3]; - int i; + char *cp[2]; RangeVar *relvar; MemoryContext oldCxt; @@ -1837,12 +1794,7 @@ plpgsql_parse_dblwordrowtype(char *word) oldCxt = MemoryContextSwitchTo(compile_tmp_cxt); /* Do case conversion and word separation */ - /* We convert %rowtype to .rowtype momentarily to keep converter happy */ - i = strlen(word) - 8; - Assert(word[i] == '%'); - word[i] = '.'; - plpgsql_convert_ident(word, cp, 3); - word[i] = '%'; + plpgsql_convert_ident(word, cp, 2); /* Lookup the relation */ relvar = makeRangeVar(cp[0], cp[1], -1); @@ -1852,12 +1804,12 @@ plpgsql_parse_dblwordrowtype(char *word) (errcode(ERRCODE_UNDEFINED_TABLE), 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); - return T_DTYPE; + + /* Build and return the row type struct */ + dtype = plpgsql_build_datatype(get_rel_type_id(classOid), -1); + + return dtype; } /* diff --git a/src/pl/plpgsql/src/pl_funcs.c b/src/pl/plpgsql/src/pl_funcs.c index 84a7633afd..12f0ae9946 100644 --- a/src/pl/plpgsql/src/pl_funcs.c +++ b/src/pl/plpgsql/src/pl_funcs.c @@ -8,7 +8,7 @@ * * * 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 bool ns_localmode = false; /* ---------- @@ -46,32 +45,6 @@ void plpgsql_ns_init(void) { 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. * + * 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 * with fewer than three components. * @@ -154,7 +129,7 @@ plpgsql_ns_additem(int itemtype, int itemno, const char *name) * ---------- */ 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, 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 */ ns_cur = nsitem->prev; diff --git a/src/pl/plpgsql/src/plpgsql.h b/src/pl/plpgsql/src/plpgsql.h index a34748475e..386d89a365 100644 --- a/src/pl/plpgsql/src/plpgsql.h +++ b/src/pl/plpgsql/src/plpgsql.h @@ -8,7 +8,7 @@ * * * 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_LookupIdentifiers; extern bool plpgsql_SpaceScanned; extern int plpgsql_nDatums; 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_dblword(const char *word); extern int plpgsql_parse_tripword(const char *word); -extern int plpgsql_parse_wordtype(char *word); -extern int plpgsql_parse_dblwordtype(char *word); -extern int plpgsql_parse_tripwordtype(char *word); -extern int plpgsql_parse_wordrowtype(char *word); -extern int plpgsql_parse_dblwordrowtype(char *word); +extern PLpgSQL_type *plpgsql_parse_wordtype(const char *word); +extern PLpgSQL_type *plpgsql_parse_dblwordtype(const char *word); +extern PLpgSQL_type *plpgsql_parse_tripwordtype(const char *word); +extern PLpgSQL_type *plpgsql_parse_wordrowtype(const char *word); +extern PLpgSQL_type *plpgsql_parse_dblwordrowtype(const char *word); extern PLpgSQL_type *plpgsql_parse_datatype(const char *string); extern PLpgSQL_type *plpgsql_build_datatype(Oid typeOid, int32 typmod); 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 bool plpgsql_ns_setlocal(bool flag); extern void plpgsql_ns_push(const char *label); extern void plpgsql_ns_pop(void); extern PLpgSQL_nsitem *plpgsql_ns_top(void); 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 *name3, int *names_used); extern PLpgSQL_nsitem *plpgsql_ns_lookup_label(PLpgSQL_nsitem *ns_cur, diff --git a/src/pl/plpgsql/src/scan.l b/src/pl/plpgsql/src/scan.l index 3563b2ff15..2c5645ce63 100644 --- a/src/pl/plpgsql/src/scan.l +++ b/src/pl/plpgsql/src/scan.l @@ -9,7 +9,7 @@ * * * 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 char *dolqstart; /* current $foo$ quote start string */ +bool plpgsql_LookupIdentifiers = true; bool plpgsql_SpaceScanned = false; %} @@ -209,52 +210,28 @@ dump { return O_DUMP; } */ {identifier} { plpgsql_error_lineno = plpgsql_scanner_lineno(); + if (!plpgsql_LookupIdentifiers) return T_WORD; return plpgsql_parse_word(yytext); } {identifier}{space}*\.{space}*{identifier} { plpgsql_error_lineno = plpgsql_scanner_lineno(); + if (!plpgsql_LookupIdentifiers) return T_DBLWORD; return plpgsql_parse_dblword(yytext); } {identifier}{space}*\.{space}*{identifier}{space}*\.{space}*{identifier} { plpgsql_error_lineno = plpgsql_scanner_lineno(); + if (!plpgsql_LookupIdentifiers) return T_TRIPWORD; 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} { plpgsql_error_lineno = plpgsql_scanner_lineno(); + if (!plpgsql_LookupIdentifiers) return T_WORD; return plpgsql_parse_word(yytext); } {param}{space}*\.{space}*{identifier} { plpgsql_error_lineno = plpgsql_scanner_lineno(); + if (!plpgsql_LookupIdentifiers) return T_DBLWORD; return plpgsql_parse_dblword(yytext); } {param}{space}*\.{space}*{identifier}{space}*\.{space}*{identifier} { plpgsql_error_lineno = plpgsql_scanner_lineno(); + if (!plpgsql_LookupIdentifiers) return T_TRIPWORD; 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; } @@ -527,6 +504,8 @@ plpgsql_scanner_init(const char *str) cur_line_start++; BEGIN(INITIAL); + plpgsql_LookupIdentifiers = true; + plpgsql_SpaceScanned = false; } /*