diff --git a/src/pl/plpgsql/src/gram.y b/src/pl/plpgsql/src/gram.y index 7c08f36b41417a01a8f611b3c58ae64e25df7b17..0a181cd07e2996fd7fd45b5360afe6b1088df5c9 100644 --- a/src/pl/plpgsql/src/gram.y +++ b/src/pl/plpgsql/src/gram.y @@ -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_NUMBER +%token T_VARIABLE /* a VAR, RECFIELD, or TRIGARG */ %token T_ROW -%token T_ROWTYPE %token T_RECORD -%token T_RECFIELD -%token T_TGARGV %token T_DTYPE +%token T_LABEL %token T_WORD -%token T_NUMBER %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, "cursor variable must be a simple variable"); + } + if (((PLpgSQL_var *) yylval.variable)->datatype->typoid != REFCURSOROID) { plpgsql_error_lineno = yylineno; - elog(ERROR, "%s must be of type cursor or refcursor", yylval.var->refname); + elog(ERROR, "%s must be of type cursor or refcursor", + ((PLpgSQL_var *) yylval.variable)->refname); } - $$ = yylval.var; + $$ = (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, "cursor variable must be a simple variable"); + } + if (((PLpgSQL_var *) yylval.variable)->datatype->typoid != REFCURSOROID) { plpgsql_error_lineno = yylineno; - elog(ERROR, "%s must be of type refcursor", yylval.var->refname); + elog(ERROR, "%s must be of type refcursor", + ((PLpgSQL_var *) yylval.variable)->refname); } - $$ = yylval.var->varno; + $$ = 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) + while(1) { + if (!have_nexttok) + tok = yylex(); + have_nexttok = 0; if (tok == ';') + break; + if (tok == 0) { - 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; + plpgsql_error_lineno = yylineno; + elog(ERROR, "unexpected end of file"); } - - if (plpgsql_SpaceScanned) - plpgsql_dstring_append(&ds, " "); - switch (tok) + if (tok == K_INTO) { - 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: + if (have_into) { - 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; + plpgsql_error_lineno = yylineno; + elog(ERROR, "INTO specified more than once"); } - break; - - default: + tok = yylex(); + switch (tok) { - if (plpgsql_SpaceScanned) - plpgsql_dstring_append(&ds, " "); - plpgsql_dstring_append(&ds, yytext); + case T_ROW: + row = yylval.row; + have_into = 1; + break; - while(1) + case T_RECORD: + rec = yylval.rec; + have_into = 1; + break; + + case T_VARIABLE: { - tok = yylex(); - if (tok == ';') - { - PLpgSQL_stmt_execsql *execsql; + int nfields = 1; + char *fieldnames[1024]; + int varnos[1024]; - 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); + check_assignable(yylval.variable); + fieldnames[0] = strdup(yytext); + varnos[0] = yylval.variable->dno; - execsql = malloc(sizeof(PLpgSQL_stmt_execsql)); - execsql->cmd_type = PLPGSQL_STMT_EXECSQL; - execsql->sqlstmt = expr; + while ((tok = yylex()) == ',') + { + tok = yylex(); + switch(tok) + { + case T_VARIABLE: + check_assignable(yylval.variable); + fieldnames[nfields] = strdup(yytext); + varnos[nfields++] = yylval.variable->dno; + break; - return (PLpgSQL_stmt *)execsql; + default: + plpgsql_error_lineno = yylineno; + elog(ERROR, "plpgsql: %s is not a variable", + yytext); + } } + have_nexttok = 1; - if (plpgsql_SpaceScanned) - plpgsql_dstring_append(&ds, " "); - switch (tok) + 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) { - 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; + row->fieldnames[nfields] = fieldnames[nfields]; + row->varnos[nfields] = varnos[nfields]; + } - case T_TGARGV: - params[nparams] = yylval.trigarg->dno; - sprintf(buf, " $%d ", ++nparams); - plpgsql_dstring_append(&ds, buf); - break; + plpgsql_adddatum((PLpgSQL_datum *)row); - default: - if (tok == 0) - { - plpgsql_error_lineno = yylineno; - elog(ERROR, "unexpected end of file"); - } - plpgsql_dstring_append(&ds, yytext); - break; - } + have_into = 1; } - } - } + break; - /************************************************************ - * Eat up the rest of the statement after the target fields - ************************************************************/ - while(1) - { - if (!have_nexttok) { - tok = yylex(); - } - have_nexttok = 0; - if (tok == ';') { - 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; + + 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; + 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; + } +} diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c index bc95eba1ed193b84412578e00b4a1e2e7b67fa7e..d3c7cb0ef02ae9a98ae5c94187a418e0959847a9 100644 --- a/src/pl/plpgsql/src/pl_comp.c +++ b/src/pl/plpgsql/src/pl_comp.c @@ -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]; - /* - * We do our lookups case insensitive - */ - cp = plpgsql_tolower(word); + /* Do case conversion and word separation */ + plpgsql_convert_ident(word, cp, 1); /* - * 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; } diff --git a/src/pl/plpgsql/src/pl_funcs.c b/src/pl/plpgsql/src/pl_funcs.c index f64604bd70fcc6d281351c6b44b5aaa83a37ddaf..6ca9f5833576bd3b24d8119ee44e717812d615aa 100644 --- a/src/pl/plpgsql/src/pl_funcs.c +++ b/src/pl/plpgsql/src/pl_funcs.c @@ -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); } diff --git a/src/pl/plpgsql/src/plpgsql.h b/src/pl/plpgsql/src/plpgsql.h index 041f2dd362aa18a32508e0375b862e44ff4b51ac..e991aa96ee754d00b97cce244dec715c0f99ff52 100644 --- a/src/pl/plpgsql/src/plpgsql.h +++ b/src/pl/plpgsql/src/plpgsql.h @@ -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); diff --git a/src/pl/plpgsql/src/scan.l b/src/pl/plpgsql/src/scan.l index 57b351f05aa3e0162a6294d38ce27bcc38f5e4dd..5f0f281ada5a3ca48b661e728f4d4c89845828e9 100644 --- a/src/pl/plpgsql/src/scan.l +++ b/src/pl/plpgsql/src/scan.l @@ -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); } - -\$[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); } - -[0-9]+ { return T_NUMBER; } +{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); } + +\${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); } + +{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; } -\*\/ { BEGIN INITIAL; } +\*\/ { BEGIN INITIAL; plpgsql_SpaceScanned = 1; } \n ; . ; <> {