Commit 73a2f6c6 authored by Tom Lane's avatar Tom Lane

More incremental refactoring in plpgsql: get rid of gram.y dependencies on

yytext.  This is a necessary change if we're going to have a lexer interface
layer that does lookahead, since yytext won't necessarily be in step with
what the grammar thinks is the current token.  yylval and yylloc should
be the only side-variables that we need to manage when doing lookahead.
parent 6ac697f1
...@@ -8,13 +8,14 @@ ...@@ -8,13 +8,14 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.133 2009/11/09 00:26:55 tgl Exp $ * $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.134 2009/11/10 02:13:13 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
#include "plpgsql.h" #include "plpgsql.h"
#include "catalog/namespace.h"
#include "catalog/pg_type.h" #include "catalog/pg_type.h"
#include "parser/parser.h" #include "parser/parser.h"
#include "parser/parse_type.h" #include "parser/parse_type.h"
...@@ -60,7 +61,12 @@ typedef struct ...@@ -60,7 +61,12 @@ typedef struct
#define parser_errposition(pos) plpgsql_scanner_errposition(pos) #define parser_errposition(pos) plpgsql_scanner_errposition(pos)
static PLpgSQL_expr *read_sql_construct(int until, union YYSTYPE; /* need forward reference for tok_is_keyword */
static bool tok_is_keyword(int token, union YYSTYPE *lval,
const char *keyword);
static void token_is_not_variable(int tok);
static PLpgSQL_expr *read_sql_construct(int until,
int until2, int until2,
int until3, int until3,
const char *expected, const char *expected,
...@@ -69,7 +75,7 @@ static PLpgSQL_expr *read_sql_construct(int until, ...@@ -69,7 +75,7 @@ static PLpgSQL_expr *read_sql_construct(int until,
bool valid_sql, bool valid_sql,
int *startloc, int *startloc,
int *endtoken); int *endtoken);
static PLpgSQL_expr *read_sql_expression2(int until, int until2, static PLpgSQL_expr *read_sql_expression2(int until, int until2,
const char *expected, const char *expected,
int *endtoken); int *endtoken);
static PLpgSQL_expr *read_sql_stmt(const char *sqlstart); static PLpgSQL_expr *read_sql_stmt(const char *sqlstart);
...@@ -83,27 +89,27 @@ static PLpgSQL_stmt *make_return_next_stmt(int location); ...@@ -83,27 +89,27 @@ static PLpgSQL_stmt *make_return_next_stmt(int location);
static PLpgSQL_stmt *make_return_query_stmt(int location); static PLpgSQL_stmt *make_return_query_stmt(int location);
static PLpgSQL_stmt *make_case(int location, PLpgSQL_expr *t_expr, static PLpgSQL_stmt *make_case(int location, PLpgSQL_expr *t_expr,
List *case_when_list, List *else_stmts); List *case_when_list, List *else_stmts);
static char *NameOfDatum(PLwdatum *wdatum);
static void check_assignable(PLpgSQL_datum *datum, int location); static void check_assignable(PLpgSQL_datum *datum, int location);
static void read_into_target(PLpgSQL_rec **rec, PLpgSQL_row **row, static void read_into_target(PLpgSQL_rec **rec, PLpgSQL_row **row,
bool *strict); bool *strict);
static PLpgSQL_row *read_into_scalar_list(const char *initial_name, static PLpgSQL_row *read_into_scalar_list(char *initial_name,
PLpgSQL_datum *initial_datum, PLpgSQL_datum *initial_datum,
int initial_location); int initial_location);
static PLpgSQL_row *make_scalar_list1(const char *initial_name, static PLpgSQL_row *make_scalar_list1(char *initial_name,
PLpgSQL_datum *initial_datum, PLpgSQL_datum *initial_datum,
int lineno, int location); int lineno, int location);
static void check_sql_expr(const char *stmt, int location, static void check_sql_expr(const char *stmt, int location,
int leaderlen); int leaderlen);
static void plpgsql_sql_error_callback(void *arg); static void plpgsql_sql_error_callback(void *arg);
static PLpgSQL_type *parse_datatype(const char *string, int location); static PLpgSQL_type *parse_datatype(const char *string, int location);
static char *parse_string_token(const char *token, int location); static char *parse_string_token(const char *token, int location);
static char *check_label(const char *yytxt);
static void check_labels(const char *start_label, static void check_labels(const char *start_label,
const char *end_label, const char *end_label,
int end_location); int end_location);
static PLpgSQL_expr *read_cursor_args(PLpgSQL_var *cursor, static PLpgSQL_expr *read_cursor_args(PLpgSQL_var *cursor,
int until, const char *expected); int until, const char *expected);
static List *read_raise_options(void); static List *read_raise_options(void);
%} %}
...@@ -112,9 +118,15 @@ static List *read_raise_options(void); ...@@ -112,9 +118,15 @@ static List *read_raise_options(void);
%locations %locations
%union { %union {
int32 ival; /* these fields must match core_YYSTYPE: */
bool boolean; int ival;
char *str; char *str;
const char *keyword;
PLword word;
PLcword cword;
PLwdatum wdatum;
bool boolean;
struct struct
{ {
char *name; char *name;
...@@ -176,7 +188,7 @@ static List *read_raise_options(void); ...@@ -176,7 +188,7 @@ static List *read_raise_options(void);
%type <forvariable> for_variable %type <forvariable> for_variable
%type <stmt> for_control %type <stmt> for_control
%type <str> any_identifier any_name opt_block_label opt_label %type <str> any_identifier opt_block_label opt_label
%type <list> proc_sect proc_stmts stmt_else %type <list> proc_sect proc_stmts stmt_else
%type <loop_body> loop_body %type <loop_body> loop_body
...@@ -197,14 +209,38 @@ static List *read_raise_options(void); ...@@ -197,14 +209,38 @@ static List *read_raise_options(void);
%type <list> getdiag_list %type <list> getdiag_list
%type <diagitem> getdiag_list_item %type <diagitem> getdiag_list_item
%type <ival> getdiag_kind getdiag_target %type <ival> getdiag_item getdiag_target
%type <ival> opt_scrollable %type <ival> opt_scrollable
%type <fetch> opt_fetch_direction %type <fetch> opt_fetch_direction
/* /*
* Keyword tokens * Basic non-keyword token types. These are hard-wired into the core lexer.
*/ * They must be listed first so that their numeric codes do not depend on
* the set of keywords. Keep this list in sync with backend/parser/gram.y!
*
* Some of these are not directly referenced in this file, but they must be
* here anyway.
*/
%token <str> IDENT FCONST SCONST BCONST XCONST Op
%token <ival> ICONST PARAM
%token TYPECAST DOT_DOT COLON_EQUALS
/*
* Other tokens recognized by plpgsql's lexer interface layer.
*/
%token T_STRING
%token T_NUMBER
%token <word> T_WORD /* unrecognized simple identifier */
%token <cword> T_CWORD /* unrecognized composite identifier */
%token <wdatum> T_DATUM /* a VAR, ROW, REC, or RECFIELD variable */
%token O_OPTION
%token O_DUMP
/*
* Keyword tokens
*/
%token K_ALIAS %token K_ALIAS
%token K_ALL %token K_ALL
%token K_ASSIGN %token K_ASSIGN
...@@ -242,33 +278,16 @@ static List *read_raise_options(void); ...@@ -242,33 +278,16 @@ static List *read_raise_options(void);
%token K_OPEN %token K_OPEN
%token K_OR %token K_OR
%token K_PERFORM %token K_PERFORM
%token K_ROW_COUNT
%token K_RAISE %token K_RAISE
%token K_RESULT_OID
%token K_RETURN %token K_RETURN
%token K_REVERSE
%token K_SCROLL %token K_SCROLL
%token K_STRICT %token K_STRICT
%token K_THEN %token K_THEN
%token K_TO %token K_TO
%token K_TYPE
%token K_USING %token K_USING
%token K_WHEN %token K_WHEN
%token K_WHILE %token K_WHILE
/*
* Other tokens
*/
%token T_STRING
%token T_NUMBER
%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
%% %%
pl_function : comp_optsect pl_block opt_semi pl_function : comp_optsect pl_block opt_semi
...@@ -362,7 +381,7 @@ decl_stmts : decl_stmts decl_stmt ...@@ -362,7 +381,7 @@ decl_stmts : decl_stmts decl_stmt
{ $$ = $1; } { $$ = $1; }
; ;
decl_stmt : '<' '<' any_name '>' '>' decl_stmt : '<' '<' any_identifier '>' '>'
{ $$ = $3; } { $$ = $3; }
| K_DECLARE | K_DECLARE
{ $$ = NULL; } { $$ = NULL; }
...@@ -540,62 +559,57 @@ decl_is_for : K_IS | /* Oracle */ ...@@ -540,62 +559,57 @@ decl_is_for : K_IS | /* Oracle */
decl_aliasitem : T_WORD decl_aliasitem : T_WORD
{ {
char *name[1];
PLpgSQL_nsitem *nsi; PLpgSQL_nsitem *nsi;
plpgsql_convert_ident(yytext, name, 1);
nsi = plpgsql_ns_lookup(plpgsql_ns_top(), false, nsi = plpgsql_ns_lookup(plpgsql_ns_top(), false,
name[0], NULL, NULL, $1.ident, NULL, NULL,
NULL); NULL);
if (nsi == NULL) if (nsi == NULL)
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[0]), $1.ident),
parser_errposition(@1))); parser_errposition(@1)));
pfree(name[0]);
$$ = nsi; $$ = nsi;
} }
| T_DBLWORD | T_CWORD
{ {
char *name[2];
PLpgSQL_nsitem *nsi; PLpgSQL_nsitem *nsi;
plpgsql_convert_ident(yytext, name, 2); if (list_length($1.idents) == 2)
nsi = plpgsql_ns_lookup(plpgsql_ns_top(), false,
nsi = plpgsql_ns_lookup(plpgsql_ns_top(), false, strVal(linitial($1.idents)),
name[0], name[1], NULL, strVal(lsecond($1.idents)),
NULL); NULL,
NULL);
else if (list_length($1.idents) == 3)
nsi = plpgsql_ns_lookup(plpgsql_ns_top(), false,
strVal(linitial($1.idents)),
strVal(lsecond($1.idents)),
strVal(lthird($1.idents)),
NULL);
else
nsi = NULL;
if (nsi == NULL) if (nsi == NULL)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT), (errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("variable \"%s.%s\" does not exist", errmsg("variable \"%s\" does not exist",
name[0], name[1]), NameListToString($1.idents)),
parser_errposition(@1))); parser_errposition(@1)));
pfree(name[0]);
pfree(name[1]);
$$ = nsi; $$ = nsi;
} }
; ;
decl_varname : T_WORD decl_varname : T_WORD
{ {
char *name; $$.name = $1.ident;
plpgsql_convert_ident(yytext, &name, 1);
$$.name = name;
$$.lineno = plpgsql_location_to_lineno(@1); $$.lineno = plpgsql_location_to_lineno(@1);
/* /*
* Check to make sure name isn't already declared * Check to make sure name isn't already declared
* in the current block. * in the current block.
*/ */
if (plpgsql_ns_lookup(plpgsql_ns_top(), true, if (plpgsql_ns_lookup(plpgsql_ns_top(), true,
name, NULL, NULL, $1.ident, NULL, NULL,
NULL) != NULL) NULL) != NULL)
yyerror("duplicate declaration"); yyerror("duplicate declaration");
} }
...@@ -748,7 +762,7 @@ getdiag_list : getdiag_list ',' getdiag_list_item ...@@ -748,7 +762,7 @@ getdiag_list : getdiag_list ',' getdiag_list_item
} }
; ;
getdiag_list_item : getdiag_target K_ASSIGN getdiag_kind getdiag_list_item : getdiag_target K_ASSIGN getdiag_item
{ {
PLpgSQL_diag_item *new; PLpgSQL_diag_item *new;
...@@ -760,44 +774,48 @@ getdiag_list_item : getdiag_target K_ASSIGN getdiag_kind ...@@ -760,44 +774,48 @@ getdiag_list_item : getdiag_target K_ASSIGN getdiag_kind
} }
; ;
getdiag_kind : K_ROW_COUNT getdiag_item :
{ {
$$ = PLPGSQL_GETDIAG_ROW_COUNT; int tok = yylex();
}
| K_RESULT_OID if (tok_is_keyword(tok, &yylval, "row_count"))
{ $$ = PLPGSQL_GETDIAG_ROW_COUNT;
$$ = PLPGSQL_GETDIAG_RESULT_OID; else if (tok_is_keyword(tok, &yylval, "result_oid"))
$$ = PLPGSQL_GETDIAG_RESULT_OID;
else
yyerror("unrecognized GET DIAGNOSTICS item");
} }
; ;
getdiag_target : T_DATUM getdiag_target : T_DATUM
{ {
check_assignable(yylval.datum, @1); check_assignable($1.datum, @1);
if (yylval.datum->dtype == PLPGSQL_DTYPE_ROW || if ($1.datum->dtype == PLPGSQL_DTYPE_ROW ||
yylval.datum->dtype == PLPGSQL_DTYPE_REC) $1.datum->dtype == PLPGSQL_DTYPE_REC)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR), (errcode(ERRCODE_SYNTAX_ERROR),
errmsg("\"%s\" is not a scalar variable", errmsg("\"%s\" is not a scalar variable",
yytext), NameOfDatum(&($1))),
parser_errposition(@1))); parser_errposition(@1)));
$$ = yylval.datum->dno; $$ = $1.datum->dno;
} }
| T_WORD | T_WORD
{ {
/* just to give a better message than "syntax error" */ /* just to give a better message than "syntax error" */
ereport(ERROR, token_is_not_variable(T_WORD);
(errcode(ERRCODE_SYNTAX_ERROR), }
errmsg("\"%s\" is not a known variable", | T_CWORD
yytext), {
parser_errposition(@1))); /* just to give a better message than "syntax error" */
token_is_not_variable(T_CWORD);
} }
; ;
assign_var : T_DATUM assign_var : T_DATUM
{ {
check_assignable(yylval.datum, @1); check_assignable($1.datum, @1);
$$ = yylval.datum->dno; $$ = $1.datum->dno;
} }
| assign_var '[' expr_until_rightbracket | assign_var '[' expr_until_rightbracket
{ {
...@@ -1057,13 +1075,12 @@ for_control : for_variable K_IN ...@@ -1057,13 +1075,12 @@ for_control : for_variable K_IN
$$ = (PLpgSQL_stmt *) new; $$ = (PLpgSQL_stmt *) new;
} }
else if (tok == T_DATUM && else if (tok == T_DATUM &&
yylval.datum->dtype == PLPGSQL_DTYPE_VAR && yylval.wdatum.datum->dtype == PLPGSQL_DTYPE_VAR &&
((PLpgSQL_var *) yylval.datum)->datatype->typoid == REFCURSOROID) ((PLpgSQL_var *) yylval.wdatum.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.datum; PLpgSQL_var *cursor = (PLpgSQL_var *) yylval.wdatum.datum;
char *varname;
new = (PLpgSQL_stmt_forc *) palloc0(sizeof(PLpgSQL_stmt_forc)); new = (PLpgSQL_stmt_forc *) palloc0(sizeof(PLpgSQL_stmt_forc));
new->cmd_type = PLPGSQL_STMT_FORC; new->cmd_type = PLPGSQL_STMT_FORC;
...@@ -1089,8 +1106,7 @@ for_control : for_variable K_IN ...@@ -1089,8 +1106,7 @@ for_control : for_variable K_IN
"LOOP"); "LOOP");
/* create loop's private RECORD variable */ /* create loop's private RECORD variable */
plpgsql_convert_ident($1.name, &varname, 1); new->rec = plpgsql_build_record($1.name,
new->rec = plpgsql_build_record(varname,
$1.lineno, $1.lineno,
true); true);
...@@ -1114,7 +1130,7 @@ for_control : for_variable K_IN ...@@ -1114,7 +1130,7 @@ for_control : for_variable K_IN
* keyword, which means it must be an * keyword, which means it must be an
* integer loop. * integer loop.
*/ */
if (tok == K_REVERSE) if (tok_is_keyword(tok, &yylval, "reverse"))
reverse = true; reverse = true;
else else
plpgsql_push_back_token(tok); plpgsql_push_back_token(tok);
...@@ -1143,7 +1159,6 @@ for_control : for_variable K_IN ...@@ -1143,7 +1159,6 @@ for_control : for_variable K_IN
PLpgSQL_expr *expr_by; PLpgSQL_expr *expr_by;
PLpgSQL_var *fvar; PLpgSQL_var *fvar;
PLpgSQL_stmt_fori *new; PLpgSQL_stmt_fori *new;
char *varname;
/* Check first expression is well-formed */ /* Check first expression is well-formed */
check_sql_expr(expr1->query, expr1loc, 7); check_sql_expr(expr1->query, expr1loc, 7);
...@@ -1168,9 +1183,8 @@ for_control : for_variable K_IN ...@@ -1168,9 +1183,8 @@ for_control : for_variable K_IN
parser_errposition(@1))); parser_errposition(@1)));
/* create loop's private variable */ /* create loop's private variable */
plpgsql_convert_ident($1.name, &varname, 1);
fvar = (PLpgSQL_var *) fvar = (PLpgSQL_var *)
plpgsql_build_variable(varname, plpgsql_build_variable($1.name,
$1.lineno, $1.lineno,
plpgsql_build_datatype(INT4OID, plpgsql_build_datatype(INT4OID,
-1), -1),
...@@ -1264,25 +1278,25 @@ for_control : for_variable K_IN ...@@ -1264,25 +1278,25 @@ for_control : for_variable K_IN
*/ */
for_variable : T_DATUM for_variable : T_DATUM
{ {
$$.name = pstrdup(yytext); $$.name = NameOfDatum(&($1));
$$.lineno = plpgsql_location_to_lineno(@1); $$.lineno = plpgsql_location_to_lineno(@1);
if (yylval.datum->dtype == PLPGSQL_DTYPE_ROW) if ($1.datum->dtype == PLPGSQL_DTYPE_ROW)
{ {
$$.scalar = NULL; $$.scalar = NULL;
$$.rec = NULL; $$.rec = NULL;
$$.row = (PLpgSQL_row *) yylval.datum; $$.row = (PLpgSQL_row *) $1.datum;
} }
else if (yylval.datum->dtype == PLPGSQL_DTYPE_REC) else if ($1.datum->dtype == PLPGSQL_DTYPE_REC)
{ {
$$.scalar = NULL; $$.scalar = NULL;
$$.rec = (PLpgSQL_rec *) yylval.datum; $$.rec = (PLpgSQL_rec *) $1.datum;
$$.row = NULL; $$.row = NULL;
} }
else else
{ {
int tok; int tok;
$$.scalar = yylval.datum; $$.scalar = $1.datum;
$$.rec = NULL; $$.rec = NULL;
$$.row = NULL; $$.row = NULL;
/* check for comma-separated list */ /* check for comma-separated list */
...@@ -1298,7 +1312,7 @@ for_variable : T_DATUM ...@@ -1298,7 +1312,7 @@ for_variable : T_DATUM
{ {
int tok; int tok;
$$.name = pstrdup(yytext); $$.name = $1.ident;
$$.lineno = plpgsql_location_to_lineno(@1); $$.lineno = plpgsql_location_to_lineno(@1);
$$.scalar = NULL; $$.scalar = NULL;
$$.rec = NULL; $$.rec = NULL;
...@@ -1307,11 +1321,19 @@ for_variable : T_DATUM ...@@ -1307,11 +1321,19 @@ for_variable : T_DATUM
tok = yylex(); tok = yylex();
plpgsql_push_back_token(tok); plpgsql_push_back_token(tok);
if (tok == ',') if (tok == ',')
{
/* can't use token_is_not_variable here */
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR), (errcode(ERRCODE_SYNTAX_ERROR),
errmsg("\"%s\" is not a known variable", errmsg("\"%s\" is not a known variable",
$$.name), $1.ident),
parser_errposition(@1))); parser_errposition(@1)));
}
}
| T_CWORD
{
/* just to give a better message than "syntax error" */
token_is_not_variable(T_CWORD);
} }
; ;
...@@ -1348,16 +1370,11 @@ stmt_return : K_RETURN ...@@ -1348,16 +1370,11 @@ stmt_return : K_RETURN
if (tok == 0) if (tok == 0)
yyerror("unexpected end of function definition"); yyerror("unexpected end of function definition");
/* if (tok_is_keyword(tok, &yylval, "next"))
* To avoid making NEXT and QUERY effectively be
* reserved words within plpgsql, recognize them
* via yytext.
*/
if (pg_strcasecmp(yytext, "next") == 0)
{ {
$$ = make_return_next_stmt(@1); $$ = make_return_next_stmt(@1);
} }
else if (pg_strcasecmp(yytext, "query") == 0) else if (tok_is_keyword(tok, &yylval, "query"))
{ {
$$ = make_return_query_stmt(@1); $$ = make_return_query_stmt(@1);
} }
...@@ -1396,35 +1413,33 @@ stmt_raise : K_RAISE ...@@ -1396,35 +1413,33 @@ stmt_raise : K_RAISE
{ {
/* /*
* First is an optional elog severity level. * First is an optional elog severity level.
* Most of these are not plpgsql keywords,
* so we rely on examining yytext.
*/ */
if (pg_strcasecmp(yytext, "exception") == 0) if (tok == K_EXCEPTION)
{ {
new->elog_level = ERROR; new->elog_level = ERROR;
tok = yylex(); tok = yylex();
} }
else if (pg_strcasecmp(yytext, "warning") == 0) else if (tok_is_keyword(tok, &yylval, "warning"))
{ {
new->elog_level = WARNING; new->elog_level = WARNING;
tok = yylex(); tok = yylex();
} }
else if (pg_strcasecmp(yytext, "notice") == 0) else if (tok_is_keyword(tok, &yylval, "notice"))
{ {
new->elog_level = NOTICE; new->elog_level = NOTICE;
tok = yylex(); tok = yylex();
} }
else if (pg_strcasecmp(yytext, "info") == 0) else if (tok_is_keyword(tok, &yylval, "info"))
{ {
new->elog_level = INFO; new->elog_level = INFO;
tok = yylex(); tok = yylex();
} }
else if (pg_strcasecmp(yytext, "log") == 0) else if (tok_is_keyword(tok, &yylval, "log"))
{ {
new->elog_level = LOG; new->elog_level = LOG;
tok = yylex(); tok = yylex();
} }
else if (pg_strcasecmp(yytext, "debug") == 0) else if (tok_is_keyword(tok, &yylval, "debug"))
{ {
new->elog_level = DEBUG1; new->elog_level = DEBUG1;
tok = yylex(); tok = yylex();
...@@ -1467,7 +1482,7 @@ stmt_raise : K_RAISE ...@@ -1467,7 +1482,7 @@ stmt_raise : K_RAISE
else if (tok != K_USING) else if (tok != K_USING)
{ {
/* must be condition name or SQLSTATE */ /* must be condition name or SQLSTATE */
if (pg_strcasecmp(yytext, "sqlstate") == 0) if (tok_is_keyword(tok, &yylval, "sqlstate"))
{ {
/* next token should be a string literal */ /* next token should be a string literal */
char *sqlstatestr; char *sqlstatestr;
...@@ -1484,14 +1499,11 @@ stmt_raise : K_RAISE ...@@ -1484,14 +1499,11 @@ stmt_raise : K_RAISE
} }
else else
{ {
char *cname;
if (tok != T_WORD) if (tok != T_WORD)
yyerror("syntax error"); yyerror("syntax error");
plpgsql_convert_ident(yytext, &cname, 1); new->condname = yylval.word.ident;
plpgsql_recognize_err_condition(cname, plpgsql_recognize_err_condition(new->condname,
false); false);
new->condname = cname;
} }
tok = yylex(); tok = yylex();
if (tok != ';' && tok != K_USING) if (tok != ';' && tok != K_USING)
...@@ -1515,18 +1527,16 @@ loop_body : proc_sect K_END K_LOOP opt_label ';' ...@@ -1515,18 +1527,16 @@ loop_body : proc_sect K_END K_LOOP opt_label ';'
; ;
/* /*
* T_WORD+T_DBLWORD+T_TRIPWORD match any initial identifier that is not a * T_WORD+T_CWORD match any initial identifier that is not a known plpgsql
* known plpgsql variable. The latter two cases are probably syntax errors, * variable. The composite case is probably a syntax error, but we'll let
* but we'll let the core parser decide that. * the core parser decide that.
*/ */
stmt_execsql : K_INSERT stmt_execsql : K_INSERT
{ $$ = make_execsql_stmt(K_INSERT, @1); } { $$ = make_execsql_stmt(K_INSERT, @1); }
| T_WORD | T_WORD
{ $$ = make_execsql_stmt(T_WORD, @1); } { $$ = make_execsql_stmt(T_WORD, @1); }
| T_DBLWORD | T_CWORD
{ $$ = make_execsql_stmt(T_DBLWORD, @1); } { $$ = make_execsql_stmt(T_CWORD, @1); }
| T_TRIPWORD
{ $$ = make_execsql_stmt(T_TRIPWORD, @1); }
; ;
stmt_dynexecute : K_EXECUTE stmt_dynexecute : K_EXECUTE
...@@ -1605,12 +1615,7 @@ stmt_open : K_OPEN cursor_variable ...@@ -1605,12 +1615,7 @@ stmt_open : K_OPEN cursor_variable
} }
if (tok != K_FOR) if (tok != K_FOR)
ereport(ERROR, yyerror("syntax error, expected \"FOR\"");
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("syntax error at \"%s\"",
yytext),
errdetail("Expected \"FOR\", to open a cursor for an unbound cursor variable."),
parser_errposition(yylloc)));
tok = yylex(); tok = yylex();
if (tok == K_EXECUTE) if (tok == K_EXECUTE)
...@@ -1705,28 +1710,29 @@ stmt_null : K_NULL ';' ...@@ -1705,28 +1710,29 @@ stmt_null : K_NULL ';'
cursor_variable : T_DATUM cursor_variable : T_DATUM
{ {
if (yylval.datum->dtype != PLPGSQL_DTYPE_VAR) if ($1.datum->dtype != PLPGSQL_DTYPE_VAR)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH), (errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("cursor variable must be a simple variable"), errmsg("cursor variable must be a simple variable"),
parser_errposition(@1))); parser_errposition(@1)));
if (((PLpgSQL_var *) yylval.datum)->datatype->typoid != REFCURSOROID) if (((PLpgSQL_var *) $1.datum)->datatype->typoid != REFCURSOROID)
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.datum)->refname), ((PLpgSQL_var *) $1.datum)->refname),
parser_errposition(@1))); parser_errposition(@1)));
$$ = (PLpgSQL_var *) yylval.datum; $$ = (PLpgSQL_var *) $1.datum;
} }
| T_WORD | T_WORD
{ {
/* just to give a better message than "syntax error" */ /* just to give a better message than "syntax error" */
ereport(ERROR, token_is_not_variable(T_WORD);
(errcode(ERRCODE_SYNTAX_ERROR), }
errmsg("\"%s\" is not a known variable", | T_CWORD
yytext), {
parser_errposition(@1))); /* just to give a better message than "syntax error" */
token_is_not_variable(T_CWORD);
} }
; ;
...@@ -1806,7 +1812,7 @@ proc_conditions : proc_conditions K_OR proc_condition ...@@ -1806,7 +1812,7 @@ proc_conditions : proc_conditions K_OR proc_condition
} }
; ;
proc_condition : any_name proc_condition : any_identifier
{ {
if (strcmp($1, "sqlstate") != 0) if (strcmp($1, "sqlstate") != 0)
{ {
...@@ -1863,7 +1869,7 @@ opt_block_label : ...@@ -1863,7 +1869,7 @@ opt_block_label :
plpgsql_ns_push(NULL); plpgsql_ns_push(NULL);
$$ = NULL; $$ = NULL;
} }
| '<' '<' any_name '>' '>' | '<' '<' any_identifier '>' '>'
{ {
plpgsql_ns_push($3); plpgsql_ns_push($3);
$$ = $3; $$ = $3;
...@@ -1876,7 +1882,9 @@ opt_label : ...@@ -1876,7 +1882,9 @@ opt_label :
} }
| any_identifier | any_identifier
{ {
$$ = check_label($1); if (plpgsql_ns_lookup_label(plpgsql_ns_top(), $1) == NULL)
yyerror("label does not exist");
$$ = $1;
} }
; ;
...@@ -1891,25 +1899,68 @@ opt_exitcond : ';' ...@@ -1891,25 +1899,68 @@ opt_exitcond : ';'
*/ */
any_identifier : T_WORD any_identifier : T_WORD
{ {
$$ = yytext; $$ = $1.ident;
} }
| T_DATUM | T_DATUM
{ {
$$ = yytext; if ($1.ident == NULL) /* composite name not OK */
} yyerror("syntax error");
; $$ = $1.ident;
any_name : any_identifier
{
char *name;
plpgsql_convert_ident($1, &name, 1);
$$ = name;
} }
; ;
%% %%
/*
* Check whether a token represents an "unreserved keyword".
* We have various places where we want to recognize a keyword in preference
* to a variable name, but not reserve that keyword in other contexts.
* Hence, this kluge. CAUTION: don't use this for reserved keywords;
* it won't recognize them.
*/
static bool
tok_is_keyword(int token, union YYSTYPE *lval, const char *keyword)
{
if (token == T_WORD)
{
/* must be unquoted and match the downcased string */
if (!lval->word.quoted && strcmp(lval->word.ident, keyword) == 0)
return true;
}
else if (token == T_DATUM)
{
/* like the T_WORD case, but also reject composite identifiers */
/* (hence an unreserved word followed by "." will not be recognized) */
if (!lval->word.quoted && lval->word.ident != NULL &&
strcmp(lval->word.ident, keyword) == 0)
return true;
}
return false; /* not the keyword */
}
/*
* Convenience routine to complain when we expected T_DATUM and got
* something else. "tok" must be the current token, since we also
* look at yylval and yylloc.
*/
static void
token_is_not_variable(int tok)
{
if (tok == T_WORD)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("\"%s\" is not a known variable",
yylval.word.ident),
parser_errposition(yylloc)));
else if (tok == T_CWORD)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("\"%s\" is not a known variable",
NameListToString(yylval.cword.idents)),
parser_errposition(yylloc)));
else
yyerror("syntax error");
}
/* Convenience routine to read an expression with one possible terminator */ /* Convenience routine to read an expression with one possible terminator */
PLpgSQL_expr * PLpgSQL_expr *
...@@ -2066,8 +2117,6 @@ read_datatype(int tok) ...@@ -2066,8 +2117,6 @@ read_datatype(int tok)
/* Should always be called with LookupIdentifiers off */ /* Should always be called with LookupIdentifiers off */
Assert(!plpgsql_LookupIdentifiers); Assert(!plpgsql_LookupIdentifiers);
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();
...@@ -2075,86 +2124,54 @@ read_datatype(int tok) ...@@ -2075,86 +2124,54 @@ read_datatype(int tok)
startlocation = yylloc; startlocation = yylloc;
/* /*
* If we have a single, double, or triple identifier, check for %TYPE * If we have a simple or composite identifier, check for %TYPE
* and %ROWTYPE constructs. * and %ROWTYPE constructs.
*/ */
if (tok == T_WORD) if (tok == T_WORD)
{ {
appendStringInfoString(&ds, yytext); char *dtname = yylval.word.ident;
tok = yylex(); tok = yylex();
if (tok == '%') if (tok == '%')
{ {
tok = yylex(); tok = yylex();
if (pg_strcasecmp(yytext, "type") == 0) if (tok_is_keyword(tok, &yylval, "type"))
{ {
result = plpgsql_parse_wordtype(ds.data); result = plpgsql_parse_wordtype(dtname);
if (result) if (result)
{
pfree(ds.data);
return result; return result;
}
} }
else if (pg_strcasecmp(yytext, "rowtype") == 0) else if (tok_is_keyword(tok, &yylval, "rowtype"))
{ {
result = plpgsql_parse_wordrowtype(ds.data); result = plpgsql_parse_wordrowtype(dtname);
if (result) if (result)
{
pfree(ds.data);
return result; return result;
}
} }
} }
} }
else if (tok == T_DBLWORD) else if (tok == T_CWORD)
{ {
appendStringInfoString(&ds, yytext); List *dtnames = yylval.cword.idents;
tok = yylex(); tok = yylex();
if (tok == '%') if (tok == '%')
{ {
tok = yylex(); tok = yylex();
if (pg_strcasecmp(yytext, "type") == 0) if (tok_is_keyword(tok, &yylval, "type"))
{
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); result = plpgsql_parse_cwordtype(dtnames);
if (result) if (result)
{
pfree(ds.data);
return result; return result;
}
} }
} else if (tok_is_keyword(tok, &yylval, "rowtype"))
}
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); result = plpgsql_parse_cwordrowtype(dtnames);
if (result) if (result)
{
pfree(ds.data);
return result; return result;
}
} }
/* there's no tripword rowtype construct */
} }
} }
/* flush temporary usage of ds for rowtype checks */
resetStringInfo(&ds);
while (tok != ';') while (tok != ';')
{ {
if (tok == 0) if (tok == 0)
...@@ -2179,6 +2196,7 @@ read_datatype(int tok) ...@@ -2179,6 +2196,7 @@ read_datatype(int tok)
} }
/* set up ds to contain complete typename text */ /* set up ds to contain complete typename text */
initStringInfo(&ds);
plpgsql_append_source_text(&ds, startlocation, yylloc); plpgsql_append_source_text(&ds, startlocation, yylloc);
type_name = ds.data; type_name = ds.data;
...@@ -2313,32 +2331,28 @@ read_fetch_direction(void) ...@@ -2313,32 +2331,28 @@ read_fetch_direction(void)
fetch->expr = NULL; fetch->expr = NULL;
fetch->returns_multiple_rows = false; fetch->returns_multiple_rows = false;
/*
* Most of the direction keywords are not plpgsql keywords, so we
* rely on examining yytext ...
*/
tok = yylex(); tok = yylex();
if (tok == 0) if (tok == 0)
yyerror("unexpected end of function definition"); yyerror("unexpected end of function definition");
if (pg_strcasecmp(yytext, "next") == 0) if (tok_is_keyword(tok, &yylval, "next"))
{ {
/* use defaults */ /* use defaults */
} }
else if (pg_strcasecmp(yytext, "prior") == 0) else if (tok_is_keyword(tok, &yylval, "prior"))
{ {
fetch->direction = FETCH_BACKWARD; fetch->direction = FETCH_BACKWARD;
} }
else if (pg_strcasecmp(yytext, "first") == 0) else if (tok_is_keyword(tok, &yylval, "first"))
{ {
fetch->direction = FETCH_ABSOLUTE; fetch->direction = FETCH_ABSOLUTE;
} }
else if (pg_strcasecmp(yytext, "last") == 0) else if (tok_is_keyword(tok, &yylval, "last"))
{ {
fetch->direction = FETCH_ABSOLUTE; fetch->direction = FETCH_ABSOLUTE;
fetch->how_many = -1; fetch->how_many = -1;
} }
else if (pg_strcasecmp(yytext, "absolute") == 0) else if (tok_is_keyword(tok, &yylval, "absolute"))
{ {
fetch->direction = FETCH_ABSOLUTE; fetch->direction = FETCH_ABSOLUTE;
fetch->expr = read_sql_expression2(K_FROM, K_IN, fetch->expr = read_sql_expression2(K_FROM, K_IN,
...@@ -2346,7 +2360,7 @@ read_fetch_direction(void) ...@@ -2346,7 +2360,7 @@ read_fetch_direction(void)
NULL); NULL);
check_FROM = false; check_FROM = false;
} }
else if (pg_strcasecmp(yytext, "relative") == 0) else if (tok_is_keyword(tok, &yylval, "relative"))
{ {
fetch->direction = FETCH_RELATIVE; fetch->direction = FETCH_RELATIVE;
fetch->expr = read_sql_expression2(K_FROM, K_IN, fetch->expr = read_sql_expression2(K_FROM, K_IN,
...@@ -2354,16 +2368,16 @@ read_fetch_direction(void) ...@@ -2354,16 +2368,16 @@ read_fetch_direction(void)
NULL); NULL);
check_FROM = false; check_FROM = false;
} }
else if (pg_strcasecmp(yytext, "all") == 0) else if (tok == K_ALL)
{ {
fetch->how_many = FETCH_ALL; fetch->how_many = FETCH_ALL;
fetch->returns_multiple_rows = true; fetch->returns_multiple_rows = true;
} }
else if (pg_strcasecmp(yytext, "forward") == 0) else if (tok_is_keyword(tok, &yylval, "forward"))
{ {
complete_direction(fetch, &check_FROM); complete_direction(fetch, &check_FROM);
} }
else if (pg_strcasecmp(yytext, "backward") == 0) else if (tok_is_keyword(tok, &yylval, "backward"))
{ {
fetch->direction = FETCH_BACKWARD; fetch->direction = FETCH_BACKWARD;
complete_direction(fetch, &check_FROM); complete_direction(fetch, &check_FROM);
...@@ -2492,9 +2506,9 @@ make_return_stmt(int location) ...@@ -2492,9 +2506,9 @@ make_return_stmt(int location)
break; break;
case T_DATUM: case T_DATUM:
if (yylval.datum->dtype == PLPGSQL_DTYPE_ROW || if (yylval.wdatum.datum->dtype == PLPGSQL_DTYPE_ROW ||
yylval.datum->dtype == PLPGSQL_DTYPE_REC) yylval.wdatum.datum->dtype == PLPGSQL_DTYPE_REC)
new->retvarno = yylval.datum->dno; new->retvarno = yylval.wdatum.datum->dno;
else else
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH), (errcode(ERRCODE_DATATYPE_MISMATCH),
...@@ -2556,9 +2570,9 @@ make_return_next_stmt(int location) ...@@ -2556,9 +2570,9 @@ make_return_next_stmt(int location)
switch (yylex()) switch (yylex())
{ {
case T_DATUM: case T_DATUM:
if (yylval.datum->dtype == PLPGSQL_DTYPE_ROW || if (yylval.wdatum.datum->dtype == PLPGSQL_DTYPE_ROW ||
yylval.datum->dtype == PLPGSQL_DTYPE_REC) yylval.wdatum.datum->dtype == PLPGSQL_DTYPE_REC)
new->retvarno = yylval.datum->dno; new->retvarno = yylval.wdatum.datum->dno;
else else
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH), (errcode(ERRCODE_DATATYPE_MISMATCH),
...@@ -2629,6 +2643,16 @@ make_return_query_stmt(int location) ...@@ -2629,6 +2643,16 @@ make_return_query_stmt(int location)
} }
/* convenience routine to fetch the name of a T_DATUM */
static char *
NameOfDatum(PLwdatum *wdatum)
{
if (wdatum->ident)
return wdatum->ident;
Assert(wdatum->idents != NIL);
return NameListToString(wdatum->idents);
}
static void static void
check_assignable(PLpgSQL_datum *datum, int location) check_assignable(PLpgSQL_datum *datum, int location)
{ {
...@@ -2685,29 +2709,26 @@ read_into_target(PLpgSQL_rec **rec, PLpgSQL_row **row, bool *strict) ...@@ -2685,29 +2709,26 @@ read_into_target(PLpgSQL_rec **rec, PLpgSQL_row **row, bool *strict)
switch (tok) switch (tok)
{ {
case T_DATUM: case T_DATUM:
if (yylval.datum->dtype == PLPGSQL_DTYPE_ROW) if (yylval.wdatum.datum->dtype == PLPGSQL_DTYPE_ROW)
{ {
check_assignable(yylval.datum, yylloc); check_assignable(yylval.wdatum.datum, yylloc);
*row = (PLpgSQL_row *) yylval.datum; *row = (PLpgSQL_row *) yylval.wdatum.datum;
} }
else if (yylval.datum->dtype == PLPGSQL_DTYPE_REC) else if (yylval.wdatum.datum->dtype == PLPGSQL_DTYPE_REC)
{ {
check_assignable(yylval.datum, yylloc); check_assignable(yylval.wdatum.datum, yylloc);
*rec = (PLpgSQL_rec *) yylval.datum; *rec = (PLpgSQL_rec *) yylval.wdatum.datum;
} }
else else
{ {
*row = read_into_scalar_list(yytext, yylval.datum, yylloc); *row = read_into_scalar_list(NameOfDatum(&(yylval.wdatum)),
yylval.wdatum.datum, yylloc);
} }
break; break;
default: default:
ereport(ERROR, /* just to give a better message than "syntax error" */
(errcode(ERRCODE_SYNTAX_ERROR), token_is_not_variable(tok);
errmsg("syntax error at \"%s\"", yytext),
errdetail("Expected record variable, row variable, "
"or list of scalar variables following INTO."),
parser_errposition(yylloc)));
} }
} }
...@@ -2718,7 +2739,7 @@ read_into_target(PLpgSQL_rec **rec, PLpgSQL_row **row, bool *strict) ...@@ -2718,7 +2739,7 @@ read_into_target(PLpgSQL_rec **rec, PLpgSQL_row **row, bool *strict)
* scalars. * scalars.
*/ */
static PLpgSQL_row * static PLpgSQL_row *
read_into_scalar_list(const char *initial_name, read_into_scalar_list(char *initial_name,
PLpgSQL_datum *initial_datum, PLpgSQL_datum *initial_datum,
int initial_location) int initial_location)
{ {
...@@ -2729,7 +2750,7 @@ read_into_scalar_list(const char *initial_name, ...@@ -2729,7 +2750,7 @@ read_into_scalar_list(const char *initial_name,
int tok; int tok;
check_assignable(initial_datum, initial_location); check_assignable(initial_datum, initial_location);
fieldnames[0] = pstrdup(initial_name); fieldnames[0] = initial_name;
varnos[0] = initial_datum->dno; varnos[0] = initial_datum->dno;
nfields = 1; nfields = 1;
...@@ -2746,24 +2767,21 @@ read_into_scalar_list(const char *initial_name, ...@@ -2746,24 +2767,21 @@ read_into_scalar_list(const char *initial_name,
switch (tok) switch (tok)
{ {
case T_DATUM: case T_DATUM:
check_assignable(yylval.datum, yylloc); check_assignable(yylval.wdatum.datum, yylloc);
if (yylval.datum->dtype == PLPGSQL_DTYPE_ROW || if (yylval.wdatum.datum->dtype == PLPGSQL_DTYPE_ROW ||
yylval.datum->dtype == PLPGSQL_DTYPE_REC) yylval.wdatum.datum->dtype == PLPGSQL_DTYPE_REC)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR), (errcode(ERRCODE_SYNTAX_ERROR),
errmsg("\"%s\" is not a scalar variable", errmsg("\"%s\" is not a scalar variable",
yytext), NameOfDatum(&(yylval.wdatum))),
parser_errposition(yylloc))); parser_errposition(yylloc)));
fieldnames[nfields] = pstrdup(yytext); fieldnames[nfields] = NameOfDatum(&(yylval.wdatum));
varnos[nfields++] = yylval.datum->dno; varnos[nfields++] = yylval.wdatum.datum->dno;
break; break;
default: default:
ereport(ERROR, /* just to give a better message than "syntax error" */
(errcode(ERRCODE_SYNTAX_ERROR), token_is_not_variable(tok);
errmsg("\"%s\" is not a known variable",
yytext),
parser_errposition(yylloc)));
} }
} }
...@@ -2800,7 +2818,7 @@ read_into_scalar_list(const char *initial_name, ...@@ -2800,7 +2818,7 @@ read_into_scalar_list(const char *initial_name,
* have it at hand already, we may as well pass it in. * have it at hand already, we may as well pass it in.
*/ */
static PLpgSQL_row * static PLpgSQL_row *
make_scalar_list1(const char *initial_name, make_scalar_list1(char *initial_name,
PLpgSQL_datum *initial_datum, PLpgSQL_datum *initial_datum,
int lineno, int location) int lineno, int location)
{ {
...@@ -2816,7 +2834,7 @@ make_scalar_list1(const char *initial_name, ...@@ -2816,7 +2834,7 @@ make_scalar_list1(const char *initial_name,
row->nfields = 1; row->nfields = 1;
row->fieldnames = palloc(sizeof(char *)); row->fieldnames = palloc(sizeof(char *));
row->varnos = palloc(sizeof(int)); row->varnos = palloc(sizeof(int));
row->fieldnames[0] = pstrdup(initial_name); row->fieldnames[0] = initial_name;
row->varnos[0] = initial_datum->dno; row->varnos[0] = initial_datum->dno;
plpgsql_adddatum((PLpgSQL_datum *)row); plpgsql_adddatum((PLpgSQL_datum *)row);
...@@ -2968,17 +2986,9 @@ parse_string_token(const char *token, int location) ...@@ -2968,17 +2986,9 @@ parse_string_token(const char *token, int location)
return result; return result;
} }
static char * /*
check_label(const char *yytxt) * Check block starting and ending labels match.
{ */
char *label_name;
plpgsql_convert_ident(yytxt, &label_name, 1);
if (plpgsql_ns_lookup_label(plpgsql_ns_top(), label_name) == NULL)
yyerror("label does not exist");
return label_name;
}
static void static void
check_labels(const char *start_label, const char *end_label, int end_location) check_labels(const char *start_label, const char *end_label, int end_location)
{ {
...@@ -3070,20 +3080,16 @@ read_raise_options(void) ...@@ -3070,20 +3080,16 @@ read_raise_options(void)
opt = (PLpgSQL_raise_option *) palloc(sizeof(PLpgSQL_raise_option)); opt = (PLpgSQL_raise_option *) palloc(sizeof(PLpgSQL_raise_option));
if (pg_strcasecmp(yytext, "errcode") == 0) if (tok_is_keyword(tok, &yylval, "errcode"))
opt->opt_type = PLPGSQL_RAISEOPTION_ERRCODE; opt->opt_type = PLPGSQL_RAISEOPTION_ERRCODE;
else if (pg_strcasecmp(yytext, "message") == 0) else if (tok_is_keyword(tok, &yylval, "message"))
opt->opt_type = PLPGSQL_RAISEOPTION_MESSAGE; opt->opt_type = PLPGSQL_RAISEOPTION_MESSAGE;
else if (pg_strcasecmp(yytext, "detail") == 0) else if (tok_is_keyword(tok, &yylval, "detail"))
opt->opt_type = PLPGSQL_RAISEOPTION_DETAIL; opt->opt_type = PLPGSQL_RAISEOPTION_DETAIL;
else if (pg_strcasecmp(yytext, "hint") == 0) else if (tok_is_keyword(tok, &yylval, "hint"))
opt->opt_type = PLPGSQL_RAISEOPTION_HINT; opt->opt_type = PLPGSQL_RAISEOPTION_HINT;
else else
ereport(ERROR, yyerror("unrecognized RAISE statement option");
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("unrecognized RAISE statement option \"%s\"",
yytext),
parser_errposition(yylloc)));
if (yylex() != K_ASSIGN) if (yylex() != K_ASSIGN)
yyerror("syntax error, expected \"=\""); yyerror("syntax error, expected \"=\"");
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.143 2009/11/09 00:26:55 tgl Exp $ * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.144 2009/11/10 02:13:13 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -1250,26 +1250,33 @@ plpgsql_parse_word(const char *word) ...@@ -1250,26 +1250,33 @@ plpgsql_parse_word(const char *word)
/* Do case conversion and word separation */ /* Do case conversion and word separation */
plpgsql_convert_ident(word, cp, 1); plpgsql_convert_ident(word, cp, 1);
/* /* No lookup if disabled */
* Do a lookup in the current namespace stack if (plpgsql_LookupIdentifiers)
*/
nse = plpgsql_ns_lookup(plpgsql_ns_top(), false,
cp[0], NULL, NULL,
NULL);
pfree(cp[0]);
if (nse != NULL)
{ {
switch (nse->itemtype) /*
* Do a lookup in the current namespace stack
*/
nse = plpgsql_ns_lookup(plpgsql_ns_top(), false,
cp[0], NULL, NULL,
NULL);
if (nse != NULL)
{ {
case PLPGSQL_NSTYPE_VAR: switch (nse->itemtype)
case PLPGSQL_NSTYPE_ROW: {
case PLPGSQL_NSTYPE_REC: case PLPGSQL_NSTYPE_VAR:
plpgsql_yylval.datum = plpgsql_Datums[nse->itemno]; case PLPGSQL_NSTYPE_ROW:
return T_DATUM; case PLPGSQL_NSTYPE_REC:
plpgsql_yylval.wdatum.datum = plpgsql_Datums[nse->itemno];
plpgsql_yylval.wdatum.ident = cp[0];
plpgsql_yylval.wdatum.quoted = (word[0] == '"');
plpgsql_yylval.wdatum.idents = NIL;
return T_DATUM;
default: default:
elog(ERROR, "unrecognized plpgsql itemtype: %d", nse->itemtype); elog(ERROR, "unrecognized plpgsql itemtype: %d",
nse->itemtype);
}
} }
} }
...@@ -1277,6 +1284,8 @@ plpgsql_parse_word(const char *word) ...@@ -1277,6 +1284,8 @@ plpgsql_parse_word(const char *word)
* Nothing found - up to now it's a word without any special meaning for * Nothing found - up to now it's a word without any special meaning for
* us. * us.
*/ */
plpgsql_yylval.word.ident = cp[0];
plpgsql_yylval.word.quoted = (word[0] == '"');
return T_WORD; return T_WORD;
} }
...@@ -1291,107 +1300,111 @@ plpgsql_parse_dblword(const char *word) ...@@ -1291,107 +1300,111 @@ plpgsql_parse_dblword(const char *word)
{ {
PLpgSQL_nsitem *ns; PLpgSQL_nsitem *ns;
char *cp[2]; char *cp[2];
List *idents;
int nnames; int nnames;
/* Do case conversion and word separation */ /* Do case conversion and word separation */
plpgsql_convert_ident(word, cp, 2); plpgsql_convert_ident(word, cp, 2);
/* idents = list_make2(makeString(cp[0]),
* Do a lookup in the current namespace stack makeString(cp[1]));
*/
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_DBLWORD;
}
switch (ns->itemtype) /* No lookup if disabled */
if (plpgsql_LookupIdentifiers)
{ {
case PLPGSQL_NSTYPE_VAR: /*
/* Block-qualified reference to scalar variable. */ * Do a lookup in the current namespace stack
plpgsql_yylval.datum = plpgsql_Datums[ns->itemno]; */
pfree(cp[0]); ns = plpgsql_ns_lookup(plpgsql_ns_top(), false,
pfree(cp[1]); cp[0], cp[1], NULL,
return T_DATUM; &nnames);
if (ns != NULL)
case PLPGSQL_NSTYPE_REC: {
if (nnames == 1) switch (ns->itemtype)
{ {
/* case PLPGSQL_NSTYPE_VAR:
* First word is a record name, so second word must be a field /* Block-qualified reference to scalar variable. */
* in this record. plpgsql_yylval.wdatum.datum = plpgsql_Datums[ns->itemno];
*/ plpgsql_yylval.wdatum.ident = NULL;
PLpgSQL_recfield *new; plpgsql_yylval.wdatum.quoted = false; /* not used */
plpgsql_yylval.wdatum.idents = idents;
new = palloc(sizeof(PLpgSQL_recfield)); return T_DATUM;
new->dtype = PLPGSQL_DTYPE_RECFIELD;
new->fieldname = pstrdup(cp[1]); case PLPGSQL_NSTYPE_REC:
new->recparentno = ns->itemno; if (nnames == 1)
{
plpgsql_adddatum((PLpgSQL_datum *) new); /*
* First word is a record name, so second word must be
* a field in this record.
*/
PLpgSQL_recfield *new;
plpgsql_yylval.datum = (PLpgSQL_datum *) new; new = palloc(sizeof(PLpgSQL_recfield));
new->dtype = PLPGSQL_DTYPE_RECFIELD;
new->fieldname = pstrdup(cp[1]);
new->recparentno = ns->itemno;
pfree(cp[0]); plpgsql_adddatum((PLpgSQL_datum *) new);
pfree(cp[1]);
return T_DATUM;
}
else
{
/* Block-qualified reference to record variable. */
plpgsql_yylval.datum = plpgsql_Datums[ns->itemno];
pfree(cp[0]);
pfree(cp[1]);
return T_DATUM;
}
case PLPGSQL_NSTYPE_ROW: plpgsql_yylval.wdatum.datum = (PLpgSQL_datum *) new;
if (nnames == 1) }
{ else
/* {
* First word is a row name, so second word must be a field in /* Block-qualified reference to record variable. */
* this row. plpgsql_yylval.wdatum.datum = plpgsql_Datums[ns->itemno];
*/ }
PLpgSQL_row *row; plpgsql_yylval.wdatum.ident = NULL;
int i; plpgsql_yylval.wdatum.quoted = false; /* not used */
plpgsql_yylval.wdatum.idents = idents;
return T_DATUM;
row = (PLpgSQL_row *) (plpgsql_Datums[ns->itemno]); case PLPGSQL_NSTYPE_ROW:
for (i = 0; i < row->nfields; i++) if (nnames == 1)
{ {
if (row->fieldnames[i] && /*
strcmp(row->fieldnames[i], cp[1]) == 0) * First word is a row name, so second word must be a
* field in this row.
*/
PLpgSQL_row *row;
int i;
row = (PLpgSQL_row *) (plpgsql_Datums[ns->itemno]);
for (i = 0; i < row->nfields; i++)
{
if (row->fieldnames[i] &&
strcmp(row->fieldnames[i], cp[1]) == 0)
{
plpgsql_yylval.wdatum.datum = plpgsql_Datums[row->varnos[i]];
plpgsql_yylval.wdatum.ident = NULL;
plpgsql_yylval.wdatum.quoted = false; /* not used */
plpgsql_yylval.wdatum.idents = idents;
return T_DATUM;
}
}
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_COLUMN),
errmsg("row \"%s\" has no field \"%s\"",
cp[0], cp[1])));
}
else
{ {
plpgsql_yylval.datum = plpgsql_Datums[row->varnos[i]]; /* Block-qualified reference to row variable. */
pfree(cp[0]); plpgsql_yylval.wdatum.datum = plpgsql_Datums[ns->itemno];
pfree(cp[1]); plpgsql_yylval.wdatum.ident = NULL;
plpgsql_yylval.wdatum.quoted = false; /* not used */
plpgsql_yylval.wdatum.idents = idents;
return T_DATUM; return T_DATUM;
} }
}
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_COLUMN),
errmsg("row \"%s\" has no field \"%s\"",
cp[0], cp[1])));
}
else
{
/* Block-qualified reference to row variable. */
plpgsql_yylval.datum = plpgsql_Datums[ns->itemno];
pfree(cp[0]);
pfree(cp[1]);
return T_DATUM;
}
default: default:
break; break;
}
}
} }
pfree(cp[0]); /* Nothing found */
pfree(cp[1]); plpgsql_yylval.cword.idents = idents;
return T_DBLWORD; return T_CWORD;
} }
...@@ -1405,90 +1418,89 @@ plpgsql_parse_tripword(const char *word) ...@@ -1405,90 +1418,89 @@ plpgsql_parse_tripword(const char *word)
{ {
PLpgSQL_nsitem *ns; PLpgSQL_nsitem *ns;
char *cp[3]; char *cp[3];
List *idents;
int nnames; int nnames;
/* Do case conversion and word separation */ /* Do case conversion and word separation */
plpgsql_convert_ident(word, cp, 3); plpgsql_convert_ident(word, cp, 3);
/* idents = list_make3(makeString(cp[0]),
* Do a lookup in the current namespace stack. Must find a qualified makeString(cp[1]),
* reference. makeString(cp[2]));
*/
ns = plpgsql_ns_lookup(plpgsql_ns_top(), false,
cp[0], cp[1], cp[2],
&nnames);
if (ns == NULL || nnames != 2)
{
pfree(cp[0]);
pfree(cp[1]);
pfree(cp[2]);
return T_TRIPWORD;
}
switch (ns->itemtype) /* No lookup if disabled */
if (plpgsql_LookupIdentifiers)
{ {
case PLPGSQL_NSTYPE_REC: /*
{ * Do a lookup in the current namespace stack. Must find a qualified
/* * reference, else ignore.
* words 1/2 are a record name, so third word must be a field */
* in this record. ns = plpgsql_ns_lookup(plpgsql_ns_top(), false,
*/ cp[0], cp[1], cp[2],
PLpgSQL_recfield *new; &nnames);
if (ns != NULL && nnames == 2)
new = palloc(sizeof(PLpgSQL_recfield)); {
new->dtype = PLPGSQL_DTYPE_RECFIELD; switch (ns->itemtype)
new->fieldname = pstrdup(cp[2]);
new->recparentno = ns->itemno;
plpgsql_adddatum((PLpgSQL_datum *) new);
plpgsql_yylval.datum = (PLpgSQL_datum *) new;
pfree(cp[0]);
pfree(cp[1]);
pfree(cp[2]);
return T_DATUM;
}
case PLPGSQL_NSTYPE_ROW:
{ {
/* case PLPGSQL_NSTYPE_REC:
* words 1/2 are a row name, so third word must be a field in {
* this row. /*
*/ * words 1/2 are a record name, so third word must be a
PLpgSQL_row *row; * field in this record.
int i; */
PLpgSQL_recfield *new;
new = palloc(sizeof(PLpgSQL_recfield));
new->dtype = PLPGSQL_DTYPE_RECFIELD;
new->fieldname = pstrdup(cp[2]);
new->recparentno = ns->itemno;
plpgsql_adddatum((PLpgSQL_datum *) new);
plpgsql_yylval.wdatum.datum = (PLpgSQL_datum *) new;
plpgsql_yylval.wdatum.ident = NULL;
plpgsql_yylval.wdatum.quoted = false; /* not used */
plpgsql_yylval.wdatum.idents = idents;
return T_DATUM;
}
row = (PLpgSQL_row *) (plpgsql_Datums[ns->itemno]); case PLPGSQL_NSTYPE_ROW:
for (i = 0; i < row->nfields; i++)
{ {
if (row->fieldnames[i] && /*
strcmp(row->fieldnames[i], cp[2]) == 0) * words 1/2 are a row name, so third word must be a field
* in this row.
*/
PLpgSQL_row *row;
int i;
row = (PLpgSQL_row *) (plpgsql_Datums[ns->itemno]);
for (i = 0; i < row->nfields; i++)
{ {
plpgsql_yylval.datum = plpgsql_Datums[row->varnos[i]]; if (row->fieldnames[i] &&
strcmp(row->fieldnames[i], cp[2]) == 0)
pfree(cp[0]); {
pfree(cp[1]); plpgsql_yylval.wdatum.datum = plpgsql_Datums[row->varnos[i]];
pfree(cp[2]); plpgsql_yylval.wdatum.ident = NULL;
plpgsql_yylval.wdatum.quoted = false; /* not used */
return T_DATUM; plpgsql_yylval.wdatum.idents = idents;
return T_DATUM;
}
} }
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_COLUMN),
errmsg("row \"%s.%s\" has no field \"%s\"",
cp[0], cp[1], cp[2])));
} }
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_COLUMN),
errmsg("row \"%s.%s\" has no field \"%s\"",
cp[0], cp[1], cp[2])));
}
default: default:
break; break;
}
}
} }
pfree(cp[0]); /* Nothing found */
pfree(cp[1]); plpgsql_yylval.cword.idents = idents;
pfree(cp[2]); return T_CWORD;
return T_TRIPWORD;
} }
...@@ -1500,26 +1512,21 @@ plpgsql_parse_tripword(const char *word) ...@@ -1500,26 +1512,21 @@ plpgsql_parse_tripword(const char *word)
* ---------- * ----------
*/ */
PLpgSQL_type * PLpgSQL_type *
plpgsql_parse_wordtype(const char *word) plpgsql_parse_wordtype(char *ident)
{ {
PLpgSQL_type *dtype; PLpgSQL_type *dtype;
PLpgSQL_nsitem *nse; PLpgSQL_nsitem *nse;
HeapTuple typeTup; HeapTuple typeTup;
char *cp[1];
/* Do case conversion and word separation */
plpgsql_convert_ident(word, cp, 1);
/* /*
* Do a lookup in the current namespace stack * Do a lookup in the current namespace stack
*/ */
nse = plpgsql_ns_lookup(plpgsql_ns_top(), false, nse = plpgsql_ns_lookup(plpgsql_ns_top(), false,
cp[0], NULL, NULL, ident, NULL, NULL,
NULL); NULL);
if (nse != NULL) if (nse != NULL)
{ {
pfree(cp[0]);
switch (nse->itemtype) switch (nse->itemtype)
{ {
case PLPGSQL_NSTYPE_VAR: case PLPGSQL_NSTYPE_VAR:
...@@ -1536,7 +1543,7 @@ plpgsql_parse_wordtype(const char *word) ...@@ -1536,7 +1543,7 @@ plpgsql_parse_wordtype(const char *word)
* Word wasn't found in the namespace stack. Try to find a data type * Word wasn't found in the namespace stack. Try to find a data type
* with that name, but ignore shell types and complex types. * with that name, but ignore shell types and complex types.
*/ */
typeTup = LookupTypeName(NULL, makeTypeName(cp[0]), NULL); typeTup = LookupTypeName(NULL, makeTypeName(ident), NULL);
if (typeTup) if (typeTup)
{ {
Form_pg_type typeStruct = (Form_pg_type) GETSTRUCT(typeTup); Form_pg_type typeStruct = (Form_pg_type) GETSTRUCT(typeTup);
...@@ -1545,14 +1552,12 @@ plpgsql_parse_wordtype(const char *word) ...@@ -1545,14 +1552,12 @@ plpgsql_parse_wordtype(const char *word)
typeStruct->typrelid != InvalidOid) typeStruct->typrelid != InvalidOid)
{ {
ReleaseSysCache(typeTup); ReleaseSysCache(typeTup);
pfree(cp[0]);
return NULL; return NULL;
} }
dtype = build_datatype(typeTup, -1); dtype = build_datatype(typeTup, -1);
ReleaseSysCache(typeTup); ReleaseSysCache(typeTup);
pfree(cp[0]);
return dtype; return dtype;
} }
...@@ -1560,134 +1565,71 @@ plpgsql_parse_wordtype(const char *word) ...@@ -1560,134 +1565,71 @@ plpgsql_parse_wordtype(const char *word)
* Nothing found - up to now it's a word without any special meaning for * Nothing found - up to now it's a word without any special meaning for
* us. * us.
*/ */
pfree(cp[0]);
return NULL; return NULL;
} }
/* ---------- /* ----------
* plpgsql_parse_dblwordtype Same lookup for word.word%TYPE * plpgsql_parse_cwordtype Same lookup for compositeword%TYPE
* ---------- * ----------
*/ */
PLpgSQL_type * PLpgSQL_type *
plpgsql_parse_dblwordtype(const char *word) plpgsql_parse_cwordtype(List *idents)
{ {
PLpgSQL_type *dtype = NULL; PLpgSQL_type *dtype = NULL;
PLpgSQL_nsitem *nse; PLpgSQL_nsitem *nse;
const char *fldname;
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[2];
MemoryContext oldCxt; MemoryContext oldCxt;
/* 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 */ if (list_length(idents) == 2)
plpgsql_convert_ident(word, cp, 2); {
/*
* 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.
*/
nse = plpgsql_ns_lookup(plpgsql_ns_top(), false,
strVal(linitial(idents)),
strVal(lsecond(idents)),
NULL,
NULL);
/* if (nse != NULL && nse->itemtype == PLPGSQL_NSTYPE_VAR)
* Do a lookup in the current namespace stack. {
* We don't need to check number of names matched, because we will only dtype = ((PLpgSQL_var *) (plpgsql_Datums[nse->itemno]))->datatype;
* consider scalar variables. goto done;
*/ }
nse = plpgsql_ns_lookup(plpgsql_ns_top(), false,
cp[0], cp[1], NULL,
NULL);
if (nse != NULL && nse->itemtype == PLPGSQL_NSTYPE_VAR) /*
* First word could also be a table name
*/
classOid = RelnameGetRelid(strVal(linitial(idents)));
if (!OidIsValid(classOid))
goto done;
fldname = strVal(lsecond(idents));
}
else if (list_length(idents) == 3)
{ {
dtype = ((PLpgSQL_var *) (plpgsql_Datums[nse->itemno]))->datatype; RangeVar *relvar;
goto done;
relvar = makeRangeVar(strVal(linitial(idents)),
strVal(lsecond(idents)),
-1);
classOid = RangeVarGetRelid(relvar, true);
if (!OidIsValid(classOid))
goto done;
fldname = strVal(lthird(idents));
} }
else
/*
* First word could also be a table name
*/
classOid = RelnameGetRelid(cp[0]);
if (!OidIsValid(classOid))
goto done;
classtup = SearchSysCache(RELOID,
ObjectIdGetDatum(classOid),
0, 0, 0);
if (!HeapTupleIsValid(classtup))
goto done;
classStruct = (Form_pg_class) GETSTRUCT(classtup);
/*
* It must be a relation, sequence, view, or type
*/
if (classStruct->relkind != RELKIND_RELATION &&
classStruct->relkind != RELKIND_SEQUENCE &&
classStruct->relkind != RELKIND_VIEW &&
classStruct->relkind != RELKIND_COMPOSITE_TYPE)
goto done;
/*
* Fetch the named table field and its type
*/
attrtup = SearchSysCacheAttName(classOid, cp[1]);
if (!HeapTupleIsValid(attrtup))
goto done;
attrStruct = (Form_pg_attribute) GETSTRUCT(attrtup);
typetup = SearchSysCache(TYPEOID,
ObjectIdGetDatum(attrStruct->atttypid),
0, 0, 0);
if (!HeapTupleIsValid(typetup))
elog(ERROR, "cache lookup failed for type %u", attrStruct->atttypid);
/*
* Found that - build a compiler type struct in the caller's cxt and
* return it
*/
MemoryContextSwitchTo(oldCxt);
dtype = build_datatype(typetup, attrStruct->atttypmod);
MemoryContextSwitchTo(compile_tmp_cxt);
done:
if (HeapTupleIsValid(classtup))
ReleaseSysCache(classtup);
if (HeapTupleIsValid(attrtup))
ReleaseSysCache(attrtup);
if (HeapTupleIsValid(typetup))
ReleaseSysCache(typetup);
MemoryContextSwitchTo(oldCxt);
return dtype;
}
/* ----------
* plpgsql_parse_tripwordtype Same lookup for word.word.word%TYPE
* ----------
*/
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[3];
RangeVar *relvar;
MemoryContext oldCxt;
/* Avoid memory leaks in the long-term function context */
oldCxt = MemoryContextSwitchTo(compile_tmp_cxt);
/* Do case conversion and word separation */
plpgsql_convert_ident(word, cp, 3);
relvar = makeRangeVar(cp[0], cp[1], -1);
classOid = RangeVarGetRelid(relvar, true);
if (!OidIsValid(classOid))
goto done; goto done;
classtup = SearchSysCache(RELOID, classtup = SearchSysCache(RELOID,
...@@ -1709,7 +1651,7 @@ plpgsql_parse_tripwordtype(const char *word) ...@@ -1709,7 +1651,7 @@ plpgsql_parse_tripwordtype(const char *word)
/* /*
* Fetch the named table field and its type * Fetch the named table field and its type
*/ */
attrtup = SearchSysCacheAttName(classOid, cp[2]); attrtup = SearchSysCacheAttName(classOid, fldname);
if (!HeapTupleIsValid(attrtup)) if (!HeapTupleIsValid(attrtup))
goto done; goto done;
attrStruct = (Form_pg_attribute) GETSTRUCT(attrtup); attrStruct = (Form_pg_attribute) GETSTRUCT(attrtup);
...@@ -1746,64 +1688,54 @@ done: ...@@ -1746,64 +1688,54 @@ done:
* ---------- * ----------
*/ */
PLpgSQL_type * PLpgSQL_type *
plpgsql_parse_wordrowtype(const char *word) plpgsql_parse_wordrowtype(char *ident)
{ {
PLpgSQL_type *dtype;
Oid classOid; Oid classOid;
char *cp[1];
/* Do case conversion and word separation */
plpgsql_convert_ident(word, cp, 1);
/* Lookup the relation */ /* Lookup the relation */
classOid = RelnameGetRelid(cp[0]); classOid = RelnameGetRelid(ident);
if (!OidIsValid(classOid)) if (!OidIsValid(classOid))
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_TABLE), (errcode(ERRCODE_UNDEFINED_TABLE),
errmsg("relation \"%s\" does not exist", cp[0]))); errmsg("relation \"%s\" does not exist", ident)));
/* Build and return the row type struct */ /* Build and return the row type struct */
dtype = plpgsql_build_datatype(get_rel_type_id(classOid), -1); return plpgsql_build_datatype(get_rel_type_id(classOid), -1);
pfree(cp[0]);
return dtype;
} }
/* ---------- /* ----------
* plpgsql_parse_dblwordrowtype Scanner found word.word%ROWTYPE. * plpgsql_parse_cwordrowtype Scanner found compositeword%ROWTYPE.
* So word must be a namespace qualified table name. * So word must be a namespace qualified table name.
* ---------- * ----------
*/ */
PLpgSQL_type * PLpgSQL_type *
plpgsql_parse_dblwordrowtype(const char *word) plpgsql_parse_cwordrowtype(List *idents)
{ {
PLpgSQL_type *dtype;
Oid classOid; Oid classOid;
char *cp[2];
RangeVar *relvar; RangeVar *relvar;
MemoryContext oldCxt; MemoryContext oldCxt;
if (list_length(idents) != 2)
return NULL;
/* Avoid memory leaks in long-term function context */ /* Avoid memory leaks in long-term function context */
oldCxt = MemoryContextSwitchTo(compile_tmp_cxt); oldCxt = MemoryContextSwitchTo(compile_tmp_cxt);
/* Do case conversion and word separation */
plpgsql_convert_ident(word, cp, 2);
/* Lookup the relation */ /* Lookup the relation */
relvar = makeRangeVar(cp[0], cp[1], -1); relvar = makeRangeVar(strVal(linitial(idents)),
strVal(lsecond(idents)),
-1);
classOid = RangeVarGetRelid(relvar, true); classOid = RangeVarGetRelid(relvar, true);
if (!OidIsValid(classOid)) if (!OidIsValid(classOid))
ereport(ERROR, ereport(ERROR,
(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",
strVal(linitial(idents)), strVal(lsecond(idents)))));
MemoryContextSwitchTo(oldCxt); MemoryContextSwitchTo(oldCxt);
/* Build and return the row type struct */ /* Build and return the row type struct */
dtype = plpgsql_build_datatype(get_rel_type_id(classOid), -1); return plpgsql_build_datatype(get_rel_type_id(classOid), -1);
return dtype;
} }
/* /*
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.122 2009/11/09 00:26:55 tgl Exp $ * $PostgreSQL: pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.123 2009/11/10 02:13:13 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -769,6 +769,27 @@ typedef struct ...@@ -769,6 +769,27 @@ typedef struct
} PLpgSQL_plugin; } PLpgSQL_plugin;
/* Struct types used during parsing */
typedef struct
{
char *ident; /* palloc'd converted identifier */
bool quoted; /* Was it double-quoted? */
} PLword;
typedef struct
{
List *idents; /* composite identifiers (list of String) */
} PLcword;
typedef struct
{
PLpgSQL_datum *datum; /* referenced variable */
char *ident; /* valid if simple name */
bool quoted;
List *idents; /* valid if composite name */
} PLwdatum;
/********************************************************************** /**********************************************************************
* Global variable declarations * Global variable declarations
**********************************************************************/ **********************************************************************/
...@@ -807,11 +828,10 @@ extern void plpgsql_parser_setup(struct ParseState *pstate, ...@@ -807,11 +828,10 @@ 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 PLpgSQL_type *plpgsql_parse_wordtype(const char *word); extern PLpgSQL_type *plpgsql_parse_wordtype(char *ident);
extern PLpgSQL_type *plpgsql_parse_dblwordtype(const char *word); extern PLpgSQL_type *plpgsql_parse_cwordtype(List *idents);
extern PLpgSQL_type *plpgsql_parse_tripwordtype(const char *word); extern PLpgSQL_type *plpgsql_parse_wordrowtype(char *ident);
extern PLpgSQL_type *plpgsql_parse_wordrowtype(const char *word); extern PLpgSQL_type *plpgsql_parse_cwordrowtype(List *idents);
extern PLpgSQL_type *plpgsql_parse_dblwordrowtype(const char *word);
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,
PLpgSQL_type *dtype, PLpgSQL_type *dtype,
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/pl/plpgsql/src/scan.l,v 1.75 2009/11/09 00:26:55 tgl Exp $ * $PostgreSQL: pgsql/src/pl/plpgsql/src/scan.l,v 1.76 2009/11/10 02:13:13 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -183,15 +183,11 @@ open { SET_YYLLOC(); return K_OPEN; } ...@@ -183,15 +183,11 @@ open { SET_YYLLOC(); return K_OPEN; }
or { SET_YYLLOC(); return K_OR; } or { SET_YYLLOC(); return K_OR; }
perform { SET_YYLLOC(); return K_PERFORM; } perform { SET_YYLLOC(); return K_PERFORM; }
raise { SET_YYLLOC(); return K_RAISE; } raise { SET_YYLLOC(); return K_RAISE; }
result_oid { SET_YYLLOC(); return K_RESULT_OID; }
return { SET_YYLLOC(); return K_RETURN; } return { SET_YYLLOC(); return K_RETURN; }
reverse { SET_YYLLOC(); return K_REVERSE; }
row_count { SET_YYLLOC(); return K_ROW_COUNT; }
scroll { SET_YYLLOC(); return K_SCROLL; } scroll { SET_YYLLOC(); return K_SCROLL; }
strict { SET_YYLLOC(); return K_STRICT; } strict { SET_YYLLOC(); return K_STRICT; }
then { SET_YYLLOC(); return K_THEN; } then { SET_YYLLOC(); return K_THEN; }
to { SET_YYLLOC(); return K_TO; } to { SET_YYLLOC(); return K_TO; }
type { SET_YYLLOC(); return K_TYPE; }
using { SET_YYLLOC(); return K_USING; } using { SET_YYLLOC(); return K_USING; }
when { SET_YYLLOC(); return K_WHEN; } when { SET_YYLLOC(); return K_WHEN; }
while { SET_YYLLOC(); return K_WHILE; } while { SET_YYLLOC(); return K_WHILE; }
...@@ -206,27 +202,21 @@ dump { SET_YYLLOC(); return O_DUMP; } ...@@ -206,27 +202,21 @@ dump { SET_YYLLOC(); return O_DUMP; }
*/ */
{identifier} { {identifier} {
SET_YYLLOC(); SET_YYLLOC();
if (!plpgsql_LookupIdentifiers) return T_WORD;
return plpgsql_parse_word(yytext); } return plpgsql_parse_word(yytext); }
{identifier}{space}*\.{space}*{identifier} { {identifier}{space}*\.{space}*{identifier} {
SET_YYLLOC(); SET_YYLLOC();
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} {
SET_YYLLOC(); SET_YYLLOC();
if (!plpgsql_LookupIdentifiers) return T_TRIPWORD;
return plpgsql_parse_tripword(yytext); } return plpgsql_parse_tripword(yytext); }
{param} { {param} {
SET_YYLLOC(); SET_YYLLOC();
if (!plpgsql_LookupIdentifiers) return T_WORD;
return plpgsql_parse_word(yytext); } return plpgsql_parse_word(yytext); }
{param}{space}*\.{space}*{identifier} { {param}{space}*\.{space}*{identifier} {
SET_YYLLOC(); SET_YYLLOC();
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} {
SET_YYLLOC(); SET_YYLLOC();
if (!plpgsql_LookupIdentifiers) return T_TRIPWORD;
return plpgsql_parse_tripword(yytext); } return plpgsql_parse_tripword(yytext); }
{digit}+ { SET_YYLLOC(); return T_NUMBER; } {digit}+ { SET_YYLLOC(); return T_NUMBER; }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment