Clean up plpgsql identifier handling: process quoted identifiers

correctly, truncate to NAMEDATALEN where needed, allow whitespace
around dots in qualified identifiers.  Get rid of T_RECFIELD and
T_TGARGV token categories, which weren't accomplishing anything
except to create room for sins of omission in the grammar, ie,
places that should have allowed them and didn't.  Fix a few other
bugs en passant.
This commit is contained in:
Tom Lane 2002-08-08 01:36:05 +00:00
parent 13e8be42bd
commit e42f8e32e9
5 changed files with 494 additions and 506 deletions

View File

@ -4,7 +4,7 @@
* procedural language
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/pl/plpgsql/src/gram.y,v 1.33 2002/05/21 18:50:16 tgl Exp $
* $Header: /cvsroot/pgsql/src/pl/plpgsql/src/gram.y,v 1.34 2002/08/08 01:36:04 tgl Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
@ -48,6 +48,7 @@ static PLpgSQL_type *read_datatype(int tok);
static PLpgSQL_stmt *make_select_stmt(void);
static PLpgSQL_stmt *make_fetch_stmt(void);
static PLpgSQL_expr *make_tupret_expr(PLpgSQL_row *row);
static void check_assignable(PLpgSQL_datum *datum);
%}
@ -83,11 +84,10 @@ static PLpgSQL_expr *make_tupret_expr(PLpgSQL_row *row);
int *initvarnos;
} declhdr;
PLpgSQL_type *dtype;
PLpgSQL_datum *variable; /* a VAR, RECFIELD, or TRIGARG */
PLpgSQL_var *var;
PLpgSQL_row *row;
PLpgSQL_rec *rec;
PLpgSQL_recfield *recfield;
PLpgSQL_trigarg *trigarg;
PLpgSQL_expr *expr;
PLpgSQL_stmt *stmt;
PLpgSQL_stmts *stmts;
@ -191,17 +191,14 @@ static PLpgSQL_expr *make_tupret_expr(PLpgSQL_row *row);
*/
%token T_FUNCTION
%token T_TRIGGER
%token T_LABEL
%token T_STRING
%token T_VARIABLE
%token T_ROW
%token T_ROWTYPE
%token T_RECORD
%token T_RECFIELD
%token T_TGARGV
%token T_DTYPE
%token T_WORD
%token T_NUMBER
%token T_VARIABLE /* a VAR, RECFIELD, or TRIGARG */
%token T_ROW
%token T_RECORD
%token T_DTYPE
%token T_LABEL
%token T_WORD
%token T_ERROR
%token O_OPTION
@ -514,16 +511,16 @@ decl_is_from : K_IS | /* Oracle */
decl_aliasitem : T_WORD
{
PLpgSQL_nsitem *nsi;
char *name;
PLpgSQL_nsitem *nsi;
plpgsql_ns_setlocal(false);
name = plpgsql_tolower(yytext);
plpgsql_convert_ident(yytext, &name, 1);
if (name[0] != '$')
{
plpgsql_error_lineno = yylineno;
elog(ERROR, "can only alias positional parameters");
}
plpgsql_ns_setlocal(false);
nsi = plpgsql_ns_lookup(name, NULL);
if (nsi == NULL)
{
@ -533,6 +530,8 @@ decl_aliasitem : T_WORD
plpgsql_ns_setlocal(true);
pfree(name);
$$ = nsi;
}
;
@ -545,16 +544,23 @@ decl_rowtype : T_ROW
decl_varname : T_WORD
{
char *name;
plpgsql_convert_ident(yytext, &name, 1);
/* name should be malloc'd for use as varname */
$$.name = strdup(plpgsql_tolower(yytext));
$$.name = strdup(name);
$$.lineno = yylineno;
pfree(name);
}
;
decl_renname : T_WORD
{
char *name;
plpgsql_convert_ident(yytext, &name, 1);
/* the result must be palloc'd, see plpgsql_ns_rename */
$$ = plpgsql_tolower(yytext);
$$ = name;
}
;
@ -808,32 +814,16 @@ getdiag_item : K_ROW_COUNT
getdiag_target : T_VARIABLE
{
if (yylval.var->isconst)
{
plpgsql_error_lineno = yylineno;
elog(ERROR, "%s is declared CONSTANT; can not receive diagnostics", yylval.var->refname);
}
$$ = yylval.var->varno;
}
| T_RECFIELD
{
$$ = yylval.recfield->rfno;
check_assignable(yylval.variable);
$$ = yylval.variable->dno;
}
;
assign_var : T_VARIABLE
{
if (yylval.var->isconst)
{
plpgsql_error_lineno = yylineno;
elog(ERROR, "%s is declared CONSTANT", yylval.var->refname);
}
$$ = yylval.var->varno;
}
| T_RECFIELD
{
$$ = yylval.recfield->rfno;
check_assignable(yylval.variable);
$$ = yylval.variable->dno;
}
;
@ -998,13 +988,23 @@ fori_var : fori_varname
fori_varname : T_VARIABLE
{
$$.name = strdup(yytext);
$$.lineno = yylineno;
char *name;
plpgsql_convert_ident(yytext, &name, 1);
/* name should be malloc'd for use as varname */
$$.name = strdup(name);
$$.lineno = yylineno;
pfree(name);
}
| T_WORD
{
$$.name = strdup(yytext);
$$.lineno = yylineno;
char *name;
plpgsql_convert_ident(yytext, &name, 1);
/* name should be malloc'd for use as varname */
$$.name = strdup(name);
$$.lineno = yylineno;
pfree(name);
}
;
@ -1254,15 +1254,7 @@ raise_params : raise_params raise_param
raise_param : ',' T_VARIABLE
{
$$ = yylval.var->varno;
}
| ',' T_RECFIELD
{
$$ = yylval.recfield->rfno;
}
| ',' T_TGARGV
{
$$ = yylval.trigarg->dno;
$$ = yylval.variable->dno;
}
;
@ -1440,23 +1432,35 @@ stmt_close : K_CLOSE lno cursor_variable ';'
cursor_varptr : T_VARIABLE
{
if (yylval.var->datatype->typoid != REFCURSOROID)
if (yylval.variable->dtype != PLPGSQL_DTYPE_VAR)
{
plpgsql_error_lineno = yylineno;
elog(ERROR, "%s must be of type cursor or refcursor", yylval.var->refname);
elog(ERROR, "cursor variable must be a simple variable");
}
$$ = yylval.var;
if (((PLpgSQL_var *) yylval.variable)->datatype->typoid != REFCURSOROID)
{
plpgsql_error_lineno = yylineno;
elog(ERROR, "%s must be of type cursor or refcursor",
((PLpgSQL_var *) yylval.variable)->refname);
}
$$ = (PLpgSQL_var *) yylval.variable;
}
;
cursor_variable : T_VARIABLE
{
if (yylval.var->datatype->typoid != REFCURSOROID)
if (yylval.variable->dtype != PLPGSQL_DTYPE_VAR)
{
plpgsql_error_lineno = yylineno;
elog(ERROR, "%s must be of type refcursor", yylval.var->refname);
elog(ERROR, "cursor variable must be a simple variable");
}
$$ = yylval.var->varno;
if (((PLpgSQL_var *) yylval.variable)->datatype->typoid != REFCURSOROID)
{
plpgsql_error_lineno = yylineno;
elog(ERROR, "%s must be of type refcursor",
((PLpgSQL_var *) yylval.variable)->refname);
}
$$ = yylval.variable->dno;
}
;
@ -1503,7 +1507,13 @@ opt_exitcond : ';'
;
opt_lblname : T_WORD
{ $$ = strdup(yytext); }
{
char *name;
plpgsql_convert_ident(yytext, &name, 1);
$$ = strdup(name);
pfree(name);
}
;
lno :
@ -1583,19 +1593,7 @@ read_sql_construct(int until,
switch (tok)
{
case T_VARIABLE:
params[nparams] = yylval.var->varno;
sprintf(buf, " $%d ", ++nparams);
plpgsql_dstring_append(&ds, buf);
break;
case T_RECFIELD:
params[nparams] = yylval.recfield->rfno;
sprintf(buf, " $%d ", ++nparams);
plpgsql_dstring_append(&ds, buf);
break;
case T_TGARGV:
params[nparams] = yylval.trigarg->dno;
params[nparams] = yylval.variable->dno;
sprintf(buf, " $%d ", ++nparams);
plpgsql_dstring_append(&ds, buf);
break;
@ -1681,10 +1679,8 @@ read_datatype(int tok)
static PLpgSQL_stmt *
make_select_stmt()
make_select_stmt(void)
{
int tok;
int lno;
PLpgSQL_dstring ds;
int nparams = 0;
int params[1024];
@ -1692,220 +1688,101 @@ make_select_stmt()
PLpgSQL_expr *expr;
PLpgSQL_row *row = NULL;
PLpgSQL_rec *rec = NULL;
PLpgSQL_stmt_select *select;
int tok = 0;
int have_nexttok = 0;
int have_into = 0;
lno = yylineno;
plpgsql_dstring_init(&ds);
plpgsql_dstring_append(&ds, "SELECT ");
while((tok = yylex()) != K_INTO)
{
if (tok == ';')
{
PLpgSQL_stmt_execsql *execsql;
expr = malloc(sizeof(PLpgSQL_expr) + sizeof(int) * nparams - sizeof(int));
expr->dtype = PLPGSQL_DTYPE_EXPR;
expr->query = strdup(plpgsql_dstring_get(&ds));
expr->plan = NULL;
expr->nparams = nparams;
while(nparams-- > 0)
expr->params[nparams] = params[nparams];
plpgsql_dstring_free(&ds);
execsql = malloc(sizeof(PLpgSQL_stmt_execsql));
execsql->cmd_type = PLPGSQL_STMT_EXECSQL;
execsql->sqlstmt = expr;
return (PLpgSQL_stmt *)execsql;
}
if (plpgsql_SpaceScanned)
plpgsql_dstring_append(&ds, " ");
switch (tok)
{
case T_VARIABLE:
params[nparams] = yylval.var->varno;
sprintf(buf, " $%d ", ++nparams);
plpgsql_dstring_append(&ds, buf);
break;
case T_RECFIELD:
params[nparams] = yylval.recfield->rfno;
sprintf(buf, " $%d ", ++nparams);
plpgsql_dstring_append(&ds, buf);
break;
case T_TGARGV:
params[nparams] = yylval.trigarg->dno;
sprintf(buf, " $%d ", ++nparams);
plpgsql_dstring_append(&ds, buf);
break;
default:
if (tok == 0)
{
plpgsql_error_lineno = yylineno;
elog(ERROR, "unexpected end of file");
}
plpgsql_dstring_append(&ds, yytext);
break;
}
}
tok = yylex();
switch (tok)
{
case T_ROW:
row = yylval.row;
break;
case T_RECORD:
rec = yylval.rec;
break;
case T_VARIABLE:
case T_RECFIELD:
{
PLpgSQL_var *var;
PLpgSQL_recfield *recfield;
int nfields = 1;
char *fieldnames[1024];
int varnos[1024];
switch (tok)
{
case T_VARIABLE:
var = yylval.var;
fieldnames[0] = strdup(yytext);
varnos[0] = var->varno;
break;
case T_RECFIELD:
recfield = yylval.recfield;
fieldnames[0] = strdup(yytext);
varnos[0] = recfield->rfno;
break;
}
while ((tok = yylex()) == ',')
{
tok = yylex();
switch(tok)
{
case T_VARIABLE:
var = yylval.var;
fieldnames[nfields] = strdup(yytext);
varnos[nfields++] = var->varno;
break;
case T_RECFIELD:
recfield = yylval.recfield;
fieldnames[0] = strdup(yytext);
varnos[0] = recfield->rfno;
break;
default:
plpgsql_error_lineno = yylineno;
elog(ERROR, "plpgsql: %s is not a variable or record field", yytext);
}
}
row = malloc(sizeof(PLpgSQL_row));
row->dtype = PLPGSQL_DTYPE_ROW;
row->refname = strdup("*internal*");
row->lineno = yylineno;
row->rowtypeclass = InvalidOid;
row->nfields = nfields;
row->fieldnames = malloc(sizeof(char *) * nfields);
row->varnos = malloc(sizeof(int) * nfields);
while (--nfields >= 0)
{
row->fieldnames[nfields] = fieldnames[nfields];
row->varnos[nfields] = varnos[nfields];
}
plpgsql_adddatum((PLpgSQL_datum *)row);
have_nexttok = 1;
}
break;
default:
{
if (plpgsql_SpaceScanned)
plpgsql_dstring_append(&ds, " ");
plpgsql_dstring_append(&ds, yytext);
while(1)
{
tok = yylex();
if (tok == ';')
{
PLpgSQL_stmt_execsql *execsql;
expr = malloc(sizeof(PLpgSQL_expr) + sizeof(int) * nparams - sizeof(int));
expr->dtype = PLPGSQL_DTYPE_EXPR;
expr->query = strdup(plpgsql_dstring_get(&ds));
expr->plan = NULL;
expr->nparams = nparams;
while (nparams-- > 0)
expr->params[nparams] = params[nparams];
plpgsql_dstring_free(&ds);
execsql = malloc(sizeof(PLpgSQL_stmt_execsql));
execsql->cmd_type = PLPGSQL_STMT_EXECSQL;
execsql->sqlstmt = expr;
return (PLpgSQL_stmt *)execsql;
}
if (plpgsql_SpaceScanned)
plpgsql_dstring_append(&ds, " ");
switch (tok)
{
case T_VARIABLE:
params[nparams] = yylval.var->varno;
sprintf(buf, " $%d ", ++nparams);
plpgsql_dstring_append(&ds, buf);
break;
case T_RECFIELD:
params[nparams] = yylval.recfield->rfno;
sprintf(buf, " $%d ", ++nparams);
plpgsql_dstring_append(&ds, buf);
break;
case T_TGARGV:
params[nparams] = yylval.trigarg->dno;
sprintf(buf, " $%d ", ++nparams);
plpgsql_dstring_append(&ds, buf);
break;
default:
if (tok == 0)
{
plpgsql_error_lineno = yylineno;
elog(ERROR, "unexpected end of file");
}
plpgsql_dstring_append(&ds, yytext);
break;
}
}
}
}
/************************************************************
* Eat up the rest of the statement after the target fields
************************************************************/
while(1)
{
if (!have_nexttok) {
if (!have_nexttok)
tok = yylex();
}
have_nexttok = 0;
if (tok == ';') {
if (tok == ';')
break;
if (tok == 0)
{
plpgsql_error_lineno = yylineno;
elog(ERROR, "unexpected end of file");
}
if (tok == K_INTO)
{
if (have_into)
{
plpgsql_error_lineno = yylineno;
elog(ERROR, "INTO specified more than once");
}
tok = yylex();
switch (tok)
{
case T_ROW:
row = yylval.row;
have_into = 1;
break;
case T_RECORD:
rec = yylval.rec;
have_into = 1;
break;
case T_VARIABLE:
{
int nfields = 1;
char *fieldnames[1024];
int varnos[1024];
check_assignable(yylval.variable);
fieldnames[0] = strdup(yytext);
varnos[0] = yylval.variable->dno;
while ((tok = yylex()) == ',')
{
tok = yylex();
switch(tok)
{
case T_VARIABLE:
check_assignable(yylval.variable);
fieldnames[nfields] = strdup(yytext);
varnos[nfields++] = yylval.variable->dno;
break;
default:
plpgsql_error_lineno = yylineno;
elog(ERROR, "plpgsql: %s is not a variable",
yytext);
}
}
have_nexttok = 1;
row = malloc(sizeof(PLpgSQL_row));
row->dtype = PLPGSQL_DTYPE_ROW;
row->refname = strdup("*internal*");
row->lineno = yylineno;
row->rowtypeclass = InvalidOid;
row->nfields = nfields;
row->fieldnames = malloc(sizeof(char *) * nfields);
row->varnos = malloc(sizeof(int) * nfields);
while (--nfields >= 0)
{
row->fieldnames[nfields] = fieldnames[nfields];
row->varnos[nfields] = varnos[nfields];
}
plpgsql_adddatum((PLpgSQL_datum *)row);
have_into = 1;
}
break;
default:
/* Treat the INTO as non-special */
plpgsql_dstring_append(&ds, " INTO ");
have_nexttok = 1;
break;
}
continue;
}
if (plpgsql_SpaceScanned)
@ -1913,29 +1790,12 @@ make_select_stmt()
switch (tok)
{
case T_VARIABLE:
params[nparams] = yylval.var->varno;
sprintf(buf, " $%d ", ++nparams);
plpgsql_dstring_append(&ds, buf);
break;
case T_RECFIELD:
params[nparams] = yylval.recfield->rfno;
sprintf(buf, " $%d ", ++nparams);
plpgsql_dstring_append(&ds, buf);
break;
case T_TGARGV:
params[nparams] = yylval.trigarg->dno;
params[nparams] = yylval.variable->dno;
sprintf(buf, " $%d ", ++nparams);
plpgsql_dstring_append(&ds, buf);
break;
default:
if (tok == 0)
{
plpgsql_error_lineno = yylineno;
elog(ERROR, "unexpected end of file");
}
plpgsql_dstring_append(&ds, yytext);
break;
}
@ -1950,19 +1810,34 @@ make_select_stmt()
expr->params[nparams] = params[nparams];
plpgsql_dstring_free(&ds);
select = malloc(sizeof(PLpgSQL_stmt_select));
memset(select, 0, sizeof(PLpgSQL_stmt_select));
select->cmd_type = PLPGSQL_STMT_SELECT;
select->rec = rec;
select->row = row;
select->query = expr;
if (have_into)
{
PLpgSQL_stmt_select *select;
return (PLpgSQL_stmt *)select;
select = malloc(sizeof(PLpgSQL_stmt_select));
memset(select, 0, sizeof(PLpgSQL_stmt_select));
select->cmd_type = PLPGSQL_STMT_SELECT;
select->rec = rec;
select->row = row;
select->query = expr;
return (PLpgSQL_stmt *)select;
}
else
{
PLpgSQL_stmt_execsql *execsql;
execsql = malloc(sizeof(PLpgSQL_stmt_execsql));
execsql->cmd_type = PLPGSQL_STMT_EXECSQL;
execsql->sqlstmt = expr;
return (PLpgSQL_stmt *)execsql;
}
}
static PLpgSQL_stmt *
make_fetch_stmt()
make_fetch_stmt(void)
{
int tok;
PLpgSQL_row *row = NULL;
@ -1970,6 +1845,8 @@ make_fetch_stmt()
PLpgSQL_stmt_fetch *fetch;
int have_nexttok = 0;
/* We have already parsed everything through the INTO keyword */
tok = yylex();
switch (tok)
{
@ -1982,28 +1859,14 @@ make_fetch_stmt()
break;
case T_VARIABLE:
case T_RECFIELD:
{
PLpgSQL_var *var;
PLpgSQL_recfield *recfield;
int nfields = 1;
char *fieldnames[1024];
int varnos[1024];
switch (tok)
{
case T_VARIABLE:
var = yylval.var;
fieldnames[0] = strdup(yytext);
varnos[0] = var->varno;
break;
case T_RECFIELD:
recfield = yylval.recfield;
fieldnames[0] = strdup(yytext);
varnos[0] = recfield->rfno;
break;
}
check_assignable(yylval.variable);
fieldnames[0] = strdup(yytext);
varnos[0] = yylval.variable->dno;
while ((tok = yylex()) == ',')
{
@ -2011,22 +1874,19 @@ make_fetch_stmt()
switch(tok)
{
case T_VARIABLE:
var = yylval.var;
check_assignable(yylval.variable);
fieldnames[nfields] = strdup(yytext);
varnos[nfields++] = var->varno;
break;
case T_RECFIELD:
recfield = yylval.recfield;
fieldnames[0] = strdup(yytext);
varnos[0] = recfield->rfno;
varnos[nfields++] = yylval.variable->dno;
break;
default:
plpgsql_error_lineno = yylineno;
elog(ERROR, "plpgsql: %s is not a variable or record field", yytext);
elog(ERROR, "plpgsql: %s is not a variable",
yytext);
}
}
have_nexttok = 1;
row = malloc(sizeof(PLpgSQL_row));
row->dtype = PLPGSQL_DTYPE_ROW;
row->refname = strdup("*internal*");
@ -2042,8 +1902,6 @@ make_fetch_stmt()
}
plpgsql_adddatum((PLpgSQL_datum *)row);
have_nexttok = 1;
}
break;
@ -2100,3 +1958,29 @@ make_tupret_expr(PLpgSQL_row *row)
plpgsql_dstring_free(&ds);
return expr;
}
static void
check_assignable(PLpgSQL_datum *datum)
{
switch (datum->dtype)
{
case PLPGSQL_DTYPE_VAR:
if (((PLpgSQL_var *) datum)->isconst)
{
plpgsql_error_lineno = yylineno;
elog(ERROR, "%s is declared CONSTANT",
((PLpgSQL_var *) datum)->refname);
}
break;
case PLPGSQL_DTYPE_RECFIELD:
/* always assignable? */
break;
case PLPGSQL_DTYPE_TRIGARG:
plpgsql_error_lineno = yylineno;
elog(ERROR, "cannot assign to tg_argv");
break;
default:
elog(ERROR, "check_assignable: unexpected datum type");
break;
}
}

View File

@ -3,7 +3,7 @@
* procedural language
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.43 2002/08/02 18:15:09 tgl Exp $
* $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.44 2002/08/08 01:36:04 tgl Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
@ -269,7 +269,8 @@ plpgsql_compile(Oid fn_oid, int functype)
row->refname = strdup(buf);
plpgsql_adddatum((PLpgSQL_datum *) row);
plpgsql_ns_additem(PLPGSQL_NSTYPE_ROW, row->rowno, buf);
plpgsql_ns_additem(PLPGSQL_NSTYPE_ROW, row->rowno,
row->refname);
arg_varnos[i] = row->rowno;
}
@ -299,7 +300,8 @@ plpgsql_compile(Oid fn_oid, int functype)
var->default_val = NULL;
plpgsql_adddatum((PLpgSQL_datum *) var);
plpgsql_ns_additem(PLPGSQL_NSTYPE_VAR, var->varno, buf);
plpgsql_ns_additem(PLPGSQL_NSTYPE_VAR, var->varno,
var->refname);
arg_varnos[i] = var->varno;
}
@ -495,7 +497,7 @@ plpgsql_compile(Oid fn_oid, int functype)
var->default_val = NULL;
plpgsql_adddatum((PLpgSQL_datum *) var);
plpgsql_ns_additem(PLPGSQL_NSTYPE_VAR, var->varno, strdup("found"));
plpgsql_ns_additem(PLPGSQL_NSTYPE_VAR, var->varno, var->refname);
function->found_varno = var->varno;
/*
@ -550,19 +552,17 @@ int
plpgsql_parse_word(char *word)
{
PLpgSQL_nsitem *nse;
char *cp;
char *cp[1];
/* Do case conversion and word separation */
plpgsql_convert_ident(word, cp, 1);
/*
* We do our lookups case insensitive
*/
cp = plpgsql_tolower(word);
/*
* Special handling when compiling triggers
* Recognize tg_argv when compiling triggers
*/
if (plpgsql_curr_compile->fn_functype == T_TRIGGER)
{
if (strcmp(cp, "tg_argv") == 0)
if (strcmp(cp[0], "tg_argv") == 0)
{
int save_spacescanned = plpgsql_SpaceScanned;
PLpgSQL_trigarg *trigarg;
@ -577,20 +577,21 @@ plpgsql_parse_word(char *word)
trigarg->argnum = plpgsql_read_expression(']', "]");
plpgsql_adddatum((PLpgSQL_datum *) trigarg);
plpgsql_yylval.trigarg = trigarg;
plpgsql_yylval.variable = (PLpgSQL_datum *) trigarg;
plpgsql_SpaceScanned = save_spacescanned;
return T_TGARGV;
pfree(cp[0]);
return T_VARIABLE;
}
}
/*
* Do a lookup on the compilers namestack
*/
nse = plpgsql_ns_lookup(cp, NULL);
nse = plpgsql_ns_lookup(cp[0], NULL);
if (nse != NULL)
{
pfree(cp);
pfree(cp[0]);
switch (nse->itemtype)
{
case PLPGSQL_NSTYPE_LABEL:
@ -617,7 +618,7 @@ plpgsql_parse_word(char *word)
* Nothing found - up to now it's a word without any special meaning
* for us.
*/
pfree(cp);
pfree(cp[0]);
return T_WORD;
}
@ -628,26 +629,22 @@ plpgsql_parse_word(char *word)
* ----------
*/
int
plpgsql_parse_dblword(char *string)
plpgsql_parse_dblword(char *word)
{
char *word1;
char *word2;
PLpgSQL_nsitem *ns;
char *cp[2];
/*
* Convert to lower case and separate the words
*/
word1 = plpgsql_tolower(string);
word2 = strchr(word1, '.');
*word2++ = '\0';
/* Do case conversion and word separation */
plpgsql_convert_ident(word, cp, 2);
/*
* Lookup the first word
*/
ns = plpgsql_ns_lookup(word1, NULL);
ns = plpgsql_ns_lookup(cp[0], NULL);
if (ns == NULL)
{
pfree(word1);
pfree(cp[0]);
pfree(cp[1]);
return T_ERROR;
}
@ -661,33 +658,29 @@ plpgsql_parse_dblword(char *string)
* only be something in a query given to the SPI manager and
* T_ERROR will get eaten up by the collector routines.
*/
ns = plpgsql_ns_lookup(word2, word1);
ns = plpgsql_ns_lookup(cp[1], cp[0]);
pfree(cp[0]);
pfree(cp[1]);
if (ns == NULL)
{
pfree(word1);
return T_ERROR;
}
switch (ns->itemtype)
{
case PLPGSQL_NSTYPE_VAR:
plpgsql_yylval.var = (PLpgSQL_var *) (plpgsql_Datums[ns->itemno]);
pfree(word1);
return T_VARIABLE;
case PLPGSQL_NSTYPE_REC:
plpgsql_yylval.rec = (PLpgSQL_rec *) (plpgsql_Datums[ns->itemno]);
pfree(word1);
return T_RECORD;
case PLPGSQL_NSTYPE_ROW:
plpgsql_yylval.row = (PLpgSQL_row *) (plpgsql_Datums[ns->itemno]);
pfree(word1);
return T_ROW;
default:
pfree(word1);
return T_ERROR;
}
break;
case PLPGSQL_NSTYPE_REC:
{
@ -699,14 +692,16 @@ plpgsql_parse_dblword(char *string)
new = malloc(sizeof(PLpgSQL_recfield));
new->dtype = PLPGSQL_DTYPE_RECFIELD;
new->fieldname = strdup(word2);
new->fieldname = strdup(cp[1]);
new->recno = ns->itemno;
plpgsql_adddatum((PLpgSQL_datum *) new);
pfree(word1);
plpgsql_yylval.recfield = new;
return T_RECFIELD;
plpgsql_yylval.variable = (PLpgSQL_datum *) new;
pfree(cp[0]);
pfree(cp[1]);
return T_VARIABLE;
}
case PLPGSQL_NSTYPE_ROW:
@ -721,22 +716,24 @@ plpgsql_parse_dblword(char *string)
row = (PLpgSQL_row *) (plpgsql_Datums[ns->itemno]);
for (i = 0; i < row->nfields; i++)
{
if (strcmp(row->fieldnames[i], word2) == 0)
if (strcmp(row->fieldnames[i], cp[1]) == 0)
{
plpgsql_yylval.var = (PLpgSQL_var *) (plpgsql_Datums[row->varnos[i]]);
pfree(word1);
pfree(cp[0]);
pfree(cp[1]);
return T_VARIABLE;
}
}
elog(ERROR, "row %s doesn't have a field %s",
word1, word2);
cp[0], cp[1]);
}
default:
break;
}
pfree(word1);
pfree(cp[0]);
pfree(cp[1]);
return T_ERROR;
}
@ -747,44 +744,42 @@ plpgsql_parse_dblword(char *string)
* ----------
*/
int
plpgsql_parse_tripword(char *string)
plpgsql_parse_tripword(char *word)
{
char *word1;
char *word2;
char *word3;
PLpgSQL_nsitem *ns;
char *cp[3];
/*
* Convert to lower case and separate the words
*/
word1 = plpgsql_tolower(string);
word2 = strchr(word1, '.');
*word2++ = '\0';
word3 = strchr(word2, '.');
*word3++ = '\0';
/* Do case conversion and word separation */
plpgsql_convert_ident(word, cp, 3);
/*
* Lookup the first word - it must be a label
*/
ns = plpgsql_ns_lookup(word1, NULL);
ns = plpgsql_ns_lookup(cp[0], NULL);
if (ns == NULL)
{
pfree(word1);
pfree(cp[0]);
pfree(cp[1]);
pfree(cp[2]);
return T_ERROR;
}
if (ns->itemtype != PLPGSQL_NSTYPE_LABEL)
{
pfree(word1);
pfree(cp[0]);
pfree(cp[1]);
pfree(cp[2]);
return T_ERROR;
}
/*
* First word is a label, so second word could be a record or row
*/
ns = plpgsql_ns_lookup(word2, word1);
ns = plpgsql_ns_lookup(cp[1], cp[0]);
if (ns == NULL)
{
pfree(word1);
pfree(cp[0]);
pfree(cp[1]);
pfree(cp[2]);
return T_ERROR;
}
@ -800,14 +795,17 @@ plpgsql_parse_tripword(char *string)
new = malloc(sizeof(PLpgSQL_recfield));
new->dtype = PLPGSQL_DTYPE_RECFIELD;
new->fieldname = strdup(word3);
new->fieldname = strdup(cp[2]);
new->recno = ns->itemno;
plpgsql_adddatum((PLpgSQL_datum *) new);
pfree(word1);
plpgsql_yylval.recfield = new;
return T_RECFIELD;
plpgsql_yylval.variable = (PLpgSQL_datum *) new;
pfree(cp[0]);
pfree(cp[1]);
pfree(cp[2]);
return T_VARIABLE;
}
case PLPGSQL_NSTYPE_ROW:
@ -822,22 +820,26 @@ plpgsql_parse_tripword(char *string)
row = (PLpgSQL_row *) (plpgsql_Datums[ns->itemno]);
for (i = 0; i < row->nfields; i++)
{
if (strcmp(row->fieldnames[i], word3) == 0)
if (strcmp(row->fieldnames[i], cp[2]) == 0)
{
plpgsql_yylval.var = (PLpgSQL_var *) (plpgsql_Datums[row->varnos[i]]);
pfree(word1);
pfree(cp[0]);
pfree(cp[1]);
pfree(cp[2]);
return T_VARIABLE;
}
}
elog(ERROR, "row %s.%s doesn't have a field %s",
word1, word2, word3);
cp[0], cp[1], cp[2]);
}
default:
break;
}
pfree(word1);
pfree(cp[0]);
pfree(cp[1]);
pfree(cp[2]);
return T_ERROR;
}
@ -851,27 +853,31 @@ int
plpgsql_parse_wordtype(char *word)
{
PLpgSQL_nsitem *nse;
char *cp;
bool old_nsstate;
Oid typeOid;
char *cp[2];
int i;
/*
* We do our lookups case insensitive
*/
cp = plpgsql_tolower(word);
*(strchr(cp, '%')) = '\0';
/* 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]);
/*
* Do a lookup on the compilers namestack. But ensure it moves up to
* the toplevel.
*/
old_nsstate = plpgsql_ns_setlocal(false);
nse = plpgsql_ns_lookup(cp, NULL);
nse = plpgsql_ns_lookup(cp[0], NULL);
plpgsql_ns_setlocal(old_nsstate);
if (nse != NULL)
{
pfree(cp);
pfree(cp[0]);
switch (nse->itemtype)
{
case PLPGSQL_NSTYPE_VAR:
@ -886,10 +892,8 @@ plpgsql_parse_wordtype(char *word)
/*
* Word wasn't found on the namestack. Try to find a data type with
* that name, but ignore pg_type entries that are in fact class types.
*
* XXX this should be improved to handle qualified-type-name references.
*/
typeOid = LookupTypeName(makeTypeName(cp));
typeOid = LookupTypeName(makeTypeName(cp[0]));
if (OidIsValid(typeOid))
{
HeapTuple typeTup;
@ -906,7 +910,7 @@ plpgsql_parse_wordtype(char *word)
typeStruct->typrelid != InvalidOid)
{
ReleaseSysCache(typeTup);
pfree(cp);
pfree(cp[0]);
return T_ERROR;
}
@ -923,7 +927,7 @@ plpgsql_parse_wordtype(char *word)
plpgsql_yylval.dtype = typ;
ReleaseSysCache(typeTup);
pfree(cp);
pfree(cp[0]);
return T_DTYPE;
}
}
@ -932,7 +936,7 @@ plpgsql_parse_wordtype(char *word)
* Nothing found - up to now it's a word without any special meaning
* for us.
*/
pfree(cp);
pfree(cp[0]);
return T_ERROR;
}
@ -942,10 +946,8 @@ plpgsql_parse_wordtype(char *word)
* ----------
*/
int
plpgsql_parse_dblwordtype(char *string)
plpgsql_parse_dblwordtype(char *word)
{
char *word1;
char *word2;
PLpgSQL_nsitem *nse;
bool old_nsstate;
Oid classOid;
@ -956,20 +958,22 @@ plpgsql_parse_dblwordtype(char *string)
HeapTuple typetup;
Form_pg_type typeStruct;
PLpgSQL_type *typ;
char *cp[3];
int i;
/*
* Convert to lower case and separate the words
*/
word1 = plpgsql_tolower(string);
word2 = strchr(word1, '.');
*word2++ = '\0';
*(strchr(word2, '%')) = '\0';
/* 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]);
/*
* Lookup the first word
*/
nse = plpgsql_ns_lookup(word1, NULL);
nse = plpgsql_ns_lookup(cp[0], NULL);
/*
* If this is a label lookup the second word in that labels namestack
@ -980,10 +984,11 @@ plpgsql_parse_dblwordtype(char *string)
if (nse->itemtype == PLPGSQL_NSTYPE_LABEL)
{
old_nsstate = plpgsql_ns_setlocal(false);
nse = plpgsql_ns_lookup(word2, word1);
nse = plpgsql_ns_lookup(cp[1], cp[0]);
plpgsql_ns_setlocal(old_nsstate);
pfree(word1);
pfree(cp[0]);
pfree(cp[1]);
if (nse != NULL)
{
@ -999,17 +1004,19 @@ plpgsql_parse_dblwordtype(char *string)
}
return T_ERROR;
}
pfree(word1);
pfree(cp[0]);
pfree(cp[1]);
return T_ERROR;
}
/*
* First word could also be a table name
*/
classOid = RelnameGetRelid(word1);
classOid = RelnameGetRelid(cp[0]);
if (!OidIsValid(classOid))
{
pfree(word1);
pfree(cp[0]);
pfree(cp[1]);
return T_ERROR;
}
classtup = SearchSysCache(RELOID,
@ -1017,7 +1024,8 @@ plpgsql_parse_dblwordtype(char *string)
0, 0, 0);
if (!HeapTupleIsValid(classtup))
{
pfree(word1);
pfree(cp[0]);
pfree(cp[1]);
return T_ERROR;
}
@ -1030,18 +1038,20 @@ plpgsql_parse_dblwordtype(char *string)
classStruct->relkind != RELKIND_VIEW)
{
ReleaseSysCache(classtup);
pfree(word1);
pfree(cp[0]);
pfree(cp[1]);
return T_ERROR;
}
/*
* Fetch the named table field and it's type
*/
attrtup = SearchSysCacheAttName(classOid, word2);
attrtup = SearchSysCacheAttName(classOid, cp[1]);
if (!HeapTupleIsValid(attrtup))
{
ReleaseSysCache(classtup);
pfree(word1);
pfree(cp[0]);
pfree(cp[1]);
return T_ERROR;
}
attrStruct = (Form_pg_attribute) GETSTRUCT(attrtup);
@ -1051,7 +1061,7 @@ plpgsql_parse_dblwordtype(char *string)
0, 0, 0);
if (!HeapTupleIsValid(typetup))
elog(ERROR, "cache lookup for type %u of %s.%s failed",
attrStruct->atttypid, word1, word2);
attrStruct->atttypid, cp[0], cp[1]);
typeStruct = (Form_pg_type) GETSTRUCT(typetup);
/*
@ -1072,7 +1082,8 @@ plpgsql_parse_dblwordtype(char *string)
ReleaseSysCache(classtup);
ReleaseSysCache(attrtup);
ReleaseSysCache(typetup);
pfree(word1);
pfree(cp[0]);
pfree(cp[1]);
return T_DTYPE;
}
@ -1083,7 +1094,7 @@ plpgsql_parse_dblwordtype(char *string)
* ----------
*/
int
plpgsql_parse_wordrowtype(char *string)
plpgsql_parse_wordrowtype(char *word)
{
Oid classOid;
HeapTuple classtup;
@ -1092,33 +1103,38 @@ plpgsql_parse_wordrowtype(char *string)
Form_pg_type typeStruct;
HeapTuple attrtup;
Form_pg_attribute attrStruct;
char *word1;
char *cp;
int i;
PLpgSQL_row *row;
PLpgSQL_var *var;
char *attname;
char *cp[2];
int i;
/* 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] = '%';
pfree(cp[1]);
/*
* Get the word in lower case and fetch the pg_class tuple.
* Fetch the pg_class tuple.
*/
word1 = plpgsql_tolower(string);
cp = strchr(word1, '%');
*cp = '\0';
classOid = RelnameGetRelid(word1);
classOid = RelnameGetRelid(cp[0]);
if (!OidIsValid(classOid))
elog(ERROR, "%s: no such class", word1);
elog(ERROR, "%s: no such class", cp[0]);
classtup = SearchSysCache(RELOID,
ObjectIdGetDatum(classOid),
0, 0, 0);
if (!HeapTupleIsValid(classtup))
elog(ERROR, "%s: no such class", word1);
elog(ERROR, "%s: no such class", cp[0]);
classStruct = (Form_pg_class) GETSTRUCT(classtup);
/* accept relation, sequence, or view pg_class entries */
if (classStruct->relkind != RELKIND_RELATION &&
classStruct->relkind != RELKIND_SEQUENCE &&
classStruct->relkind != RELKIND_VIEW)
elog(ERROR, "%s isn't a table", word1);
elog(ERROR, "%s isn't a table", cp[0]);
/*
* Create a row datum entry and all the required variables that it
@ -1144,17 +1160,17 @@ plpgsql_parse_wordrowtype(char *string)
0, 0);
if (!HeapTupleIsValid(attrtup))
elog(ERROR, "cache lookup for attribute %d of class %s failed",
i + 1, word1);
i + 1, cp[0]);
attrStruct = (Form_pg_attribute) GETSTRUCT(attrtup);
cp = pstrdup(NameStr(attrStruct->attname));
attname = pstrdup(NameStr(attrStruct->attname));
typetup = SearchSysCache(TYPEOID,
ObjectIdGetDatum(attrStruct->atttypid),
0, 0, 0);
if (!HeapTupleIsValid(typetup))
elog(ERROR, "cache lookup for type %u of %s.%s failed",
attrStruct->atttypid, word1, cp);
attrStruct->atttypid, cp[0], attname);
typeStruct = (Form_pg_type) GETSTRUCT(typetup);
/*
@ -1170,10 +1186,10 @@ plpgsql_parse_wordrowtype(char *string)
var = malloc(sizeof(PLpgSQL_var));
memset(var, 0, sizeof(PLpgSQL_var));
var->dtype = PLPGSQL_DTYPE_VAR;
var->refname = malloc(strlen(word1) + strlen(cp) + 2);
strcpy(var->refname, word1);
var->refname = malloc(strlen(cp[0]) + strlen(attname) + 2);
strcpy(var->refname, cp[0]);
strcat(var->refname, ".");
strcat(var->refname, cp);
strcat(var->refname, attname);
var->datatype = malloc(sizeof(PLpgSQL_type));
var->datatype->typname = strdup(NameStr(typeStruct->typname));
var->datatype->typoid = attrStruct->atttypid;
@ -1197,7 +1213,7 @@ plpgsql_parse_wordrowtype(char *string)
/*
* Add the variable to the row.
*/
row->fieldnames[i] = strdup(cp);
row->fieldnames[i] = strdup(attname);
row->varnos[i] = var->varno;
}

View File

@ -3,7 +3,7 @@
* procedural language
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_funcs.c,v 1.18 2002/05/05 17:38:26 tgl Exp $
* $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_funcs.c,v 1.19 2002/08/08 01:36:05 tgl Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
@ -46,6 +46,10 @@
#include "plpgsql.h"
#include "pl.tab.h"
#ifdef MULTIBYTE
#include "mb/pg_wchar.h"
#endif
/* ----------
* Local variables for the namestack handling
@ -152,6 +156,9 @@ plpgsql_ns_push(char *label)
{
PLpgSQL_ns *new;
if (label == NULL)
label = "";
new = palloc(sizeof(PLpgSQL_ns));
memset(new, 0, sizeof(PLpgSQL_ns));
new->upper = ns_current;
@ -192,9 +199,7 @@ plpgsql_ns_additem(int itemtype, int itemno, char *name)
PLpgSQL_ns *ns = ns_current;
PLpgSQL_nsitem *nse;
if (name == NULL)
name = "";
name = plpgsql_tolower(name);
Assert(name != NULL);
if (ns->items_used == ns->items_alloc)
{
@ -322,46 +327,115 @@ plpgsql_ns_rename(char *oldname, char *newname)
/* ----------
* plpgsql_tolower Translate a string to lower case
* but honor "" escaping.
* plpgsql_convert_ident
*
* Convert a possibly-qualified identifier to internal form: handle
* double quotes, translate to lower case where not inside quotes,
* truncate to NAMEDATALEN.
*
* There may be several identifiers separated by dots and optional
* whitespace. Each one is converted to a separate palloc'd string.
* The caller passes the expected number of identifiers, as well as
* a char* array to hold them. It is an error if we find the wrong
* number of identifiers (cf grammar processing of fori_varname).
*
* NOTE: the input string has already been accepted by the flex lexer,
* so we don't need a heckuva lot of error checking here.
* ----------
*/
char *
plpgsql_tolower(char *s)
void
plpgsql_convert_ident(const char *s, char **output, int numidents)
{
char *sstart = s;
char *ret;
char *cp;
ret = palloc(strlen(s) + 1);
cp = ret;
const char *sstart = s;
int identctr = 0;
/* Outer loop over identifiers */
while (*s)
{
char *curident;
char *cp;
int i;
/* Process current identifier */
curident = palloc(strlen(s) + 1); /* surely enough room */
cp = curident;
if (*s == '"')
{
/* Quoted identifier: copy, collapsing out doubled quotes */
s++;
while (*s)
{
if (*s == '"')
break;
{
if (s[1] != '"')
break;
s++;
}
*cp++ = *s++;
}
if (*s != '"')
elog(ERROR, "unterminated \" in name %s", sstart);
if (*s != '"') /* should not happen if lexer checked */
elog(ERROR, "unterminated \" in name: %s", sstart);
s++;
}
else
{
if (isupper((unsigned char) *s))
*cp++ = tolower((unsigned char) *s++);
else
*cp++ = *s++;
/*
* Normal identifier: downcase, stop at dot or whitespace.
*
* Note that downcasing is locale-sensitive, following SQL99
* rules for identifiers. We have already decided that the
* item is not a PLPGSQL keyword.
*/
while (*s && *s != '.' && !isspace((unsigned char) *s))
{
if (isupper((unsigned char) *s))
*cp++ = tolower((unsigned char) *s++);
else
*cp++ = *s++;
}
}
/* Truncate to NAMEDATALEN */
*cp = '\0';
i = cp - curident;
if (i >= NAMEDATALEN)
{
int len;
#ifdef MULTIBYTE
len = pg_mbcliplen(curident, i, NAMEDATALEN-1);
#else
len = NAMEDATALEN-1;
#endif
curident[len] = '\0';
}
/* Pass ident to caller */
if (identctr < numidents)
output[identctr++] = curident;
else
elog(ERROR, "Qualified identifier cannot be used here: %s",
sstart);
/* If not done, skip whitespace, dot, whitespace */
if (*s)
{
while (*s && isspace((unsigned char) *s))
s++;
if (*s++ != '.')
elog(ERROR, "Expected dot between identifiers: %s", sstart);
while (*s && isspace((unsigned char) *s))
s++;
if (*s == '\0')
elog(ERROR, "Expected another identifier: %s", sstart);
}
}
*cp = '\0';
return ret;
if (identctr != numidents)
elog(ERROR, "Improperly qualified identifier: %s",
sstart);
}

View File

@ -3,7 +3,7 @@
* procedural language
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.24 2001/11/29 22:57:37 tgl Exp $
* $Header: /cvsroot/pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.25 2002/08/08 01:36:05 tgl Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
@ -547,11 +547,11 @@ extern PLpgSQL_function *plpgsql_curr_compile;
*/
extern PLpgSQL_function *plpgsql_compile(Oid fn_oid, int functype);
extern int plpgsql_parse_word(char *word);
extern int plpgsql_parse_dblword(char *string);
extern int plpgsql_parse_tripword(char *string);
extern int plpgsql_parse_wordtype(char *string);
extern int plpgsql_parse_dblwordtype(char *string);
extern int plpgsql_parse_wordrowtype(char *string);
extern int plpgsql_parse_dblword(char *word);
extern int plpgsql_parse_tripword(char *word);
extern int plpgsql_parse_wordtype(char *word);
extern int plpgsql_parse_dblwordtype(char *word);
extern int plpgsql_parse_wordrowtype(char *word);
extern PLpgSQL_type *plpgsql_parse_datatype(char *string);
extern void plpgsql_adddatum(PLpgSQL_datum * new);
extern int plpgsql_add_initdatums(int **varnos);
@ -598,7 +598,7 @@ extern void plpgsql_ns_rename(char *oldname, char *newname);
* Other functions in pl_funcs.c
* ----------
*/
extern char *plpgsql_tolower(char *s);
extern void plpgsql_convert_ident(const char *s, char **output, int numidents);
extern const char *plpgsql_stmt_typename(PLpgSQL_stmt * stmt);
extern void plpgsql_dumptree(PLpgSQL_function * func);

View File

@ -4,7 +4,7 @@
* procedural language
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/pl/plpgsql/src/Attic/scan.l,v 1.20 2002/08/04 04:17:33 momjian Exp $
* $Header: /cvsroot/pgsql/src/pl/plpgsql/src/Attic/scan.l,v 1.21 2002/08/08 01:36:05 tgl Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
@ -60,13 +60,21 @@ static void plpgsql_input(char *buf, int *result, int max);
%option noyywrap
%option yylineno
%option case-insensitive
WS [\200-\377_A-Za-z"]
WC [\200-\377_A-Za-z0-9"]
%x IN_STRING IN_COMMENT
digit [0-9]
letter [\200-\377_A-Za-z]
letter_or_digit [\200-\377_A-Za-z0-9]
quoted_ident (\"[^\"]*\")+
identifier ({letter}{letter_or_digit}*|{quoted_ident})
space [ \t\n\r\f]
%%
/* ----------
* Local variable in scanner to remember where
@ -154,37 +162,43 @@ dump { return O_DUMP; }
* Special word rules
* ----------
*/
{WS}{WC}* { return plpgsql_parse_word(yytext); }
{WS}{WC}*\.{WS}{WC}* { return plpgsql_parse_dblword(yytext); }
{WS}{WC}*\.{WS}{WC}*\.{WS}{WC}* { return plpgsql_parse_tripword(yytext); }
{WS}{WC}*%TYPE { return plpgsql_parse_wordtype(yytext); }
{WS}{WC}*\.{WS}{WC}*%TYPE { return plpgsql_parse_dblwordtype(yytext); }
{WS}{WC}*%ROWTYPE { return plpgsql_parse_wordrowtype(yytext); }
{identifier} { return plpgsql_parse_word(yytext); }
{identifier}{space}*\.{space}*{identifier} { return plpgsql_parse_dblword(yytext); }
{identifier}{space}*\.{space}*{identifier}{space}*\.{space}*{identifier} { return plpgsql_parse_tripword(yytext); }
{identifier}{space}*%TYPE { return plpgsql_parse_wordtype(yytext); }
{identifier}{space}*\.{space}*{identifier}{space}*%TYPE { return plpgsql_parse_dblwordtype(yytext); }
{identifier}{space}*%ROWTYPE { return plpgsql_parse_wordrowtype(yytext); }
\$[0-9]+ { return plpgsql_parse_word(yytext); }
\$[0-9]+\.{WS}{WC}* { return plpgsql_parse_dblword(yytext); }
\$[0-9]+\.{WS}{WC}*\.{WS}{WC}* { return plpgsql_parse_tripword(yytext); }
\$[0-9]+%TYPE { return plpgsql_parse_wordtype(yytext); }
\$[0-9]+\.{WS}{WC}*%TYPE { return plpgsql_parse_dblwordtype(yytext); }
\$[0-9]+%ROWTYPE { return plpgsql_parse_wordrowtype(yytext); }
\${digit}+ { return plpgsql_parse_word(yytext); }
\${digit}+{space}*\.{space}*{identifier} { return plpgsql_parse_dblword(yytext); }
\${digit}+{space}*\.{space}*{identifier}{space}*\.{space}*{identifier} { return plpgsql_parse_tripword(yytext); }
\${digit}+{space}*%TYPE { return plpgsql_parse_wordtype(yytext); }
\${digit}+{space}*\.{space}*{identifier}{space}*%TYPE { return plpgsql_parse_dblwordtype(yytext); }
\${digit}+{space}*%ROWTYPE { return plpgsql_parse_wordrowtype(yytext); }
[0-9]+ { return T_NUMBER; }
{digit}+ { return T_NUMBER; }
\". {
plpgsql_error_lineno = yylineno;
elog(ERROR, "unterminated quoted identifier");
}
/* ----------
* Ignore whitespaces but remember this happened
* ----------
*/
[ \t\r\n]+ { plpgsql_SpaceScanned = 1; }
{space}+ { plpgsql_SpaceScanned = 1; }
/* ----------
* Eat up comments
* ----------
*/
--[^\r\n]* ;
\/\* { start_lineno = yylineno;
BEGIN IN_COMMENT;
}
<IN_COMMENT>\*\/ { BEGIN INITIAL; }
<IN_COMMENT>\*\/ { BEGIN INITIAL; plpgsql_SpaceScanned = 1; }
<IN_COMMENT>\n ;
<IN_COMMENT>. ;
<IN_COMMENT><<EOF>> {