Clean up plpgsql identifier handling: process quoted identifiers
authorTom Lane <tgl@sss.pgh.pa.us>
Thu, 8 Aug 2002 01:36:05 +0000 (01:36 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Thu, 8 Aug 2002 01:36:05 +0000 (01:36 +0000)
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.

src/pl/plpgsql/src/gram.y
src/pl/plpgsql/src/pl_comp.c
src/pl/plpgsql/src/pl_funcs.c
src/pl/plpgsql/src/plpgsql.h
src/pl/plpgsql/src/scan.l

index 7c08f36b41417a01a8f611b3c58ae64e25df7b17..0a181cd07e2996fd7fd45b5360afe6b1088df5c9 100644 (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_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;
+       }
+}
index bc95eba1ed193b84412578e00b4a1e2e7b67fa7e..d3c7cb0ef02ae9a98ae5c94187a418e0959847a9 100644 (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];
 
-       /*
-        * 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;
        }
 
index f64604bd70fcc6d281351c6b44b5aaa83a37ddaf..6ca9f5833576bd3b24d8119ee44e717812d615aa 100644 (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.
  *
 #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);
 }
 
 
index 041f2dd362aa18a32508e0375b862e44ff4b51ac..e991aa96ee754d00b97cce244dec715c0f99ff52 100644 (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);
 
index 57b351f05aa3e0162a6294d38ce27bcc38f5e4dd..5f0f281ada5a3ca48b661e728f4d4c89845828e9 100644 (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);     }
-
-\$[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;
                        }
-<IN_COMMENT>\*\/       { BEGIN INITIAL;                        }
+<IN_COMMENT>\*\/       { BEGIN INITIAL; plpgsql_SpaceScanned = 1; }
 <IN_COMMENT>\n         ;
 <IN_COMMENT>.          ;
 <IN_COMMENT><<EOF>>    {