Clean up plpgsql identifier handling: process quoted identifiers
authorTom Lane
Thu, 8 Aug 2002 01:36:05 +0000 (01:36 +0000)
committerTom Lane
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;
            }
-\*\/   { BEGIN INITIAL;            }
+\*\/   { BEGIN INITIAL; plpgsql_SpaceScanned = 1; }
 \n     ;
 .      ;
 <>    {