aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/pl/plpgsql/src/gram.y510
-rw-r--r--src/pl/plpgsql/src/pl_comp.c266
-rw-r--r--src/pl/plpgsql/src/pl_funcs.c120
-rw-r--r--src/pl/plpgsql/src/plpgsql.h14
-rw-r--r--src/pl/plpgsql/src/scan.l56
5 files changed, 477 insertions, 489 deletions
diff --git a/src/pl/plpgsql/src/gram.y b/src/pl/plpgsql/src/gram.y
index 7c08f36b414..0a181cd07e2 100644
--- a/src/pl/plpgsql/src/gram.y
+++ b/src/pl/plpgsql/src/gram.y
@@ -4,7 +4,7 @@
* procedural language
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/gram.y,v 1.33 2002/05/21 18:50:16 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/gram.y,v 1.34 2002/08/08 01:36:04 tgl Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
@@ -48,6 +48,7 @@ static PLpgSQL_type *read_datatype(int tok);
static PLpgSQL_stmt *make_select_stmt(void);
static PLpgSQL_stmt *make_fetch_stmt(void);
static PLpgSQL_expr *make_tupret_expr(PLpgSQL_row *row);
+static void check_assignable(PLpgSQL_datum *datum);
%}
@@ -83,11 +84,10 @@ static PLpgSQL_expr *make_tupret_expr(PLpgSQL_row *row);
int *initvarnos;
} declhdr;
PLpgSQL_type *dtype;
+ PLpgSQL_datum *variable; /* a VAR, RECFIELD, or TRIGARG */
PLpgSQL_var *var;
PLpgSQL_row *row;
PLpgSQL_rec *rec;
- PLpgSQL_recfield *recfield;
- PLpgSQL_trigarg *trigarg;
PLpgSQL_expr *expr;
PLpgSQL_stmt *stmt;
PLpgSQL_stmts *stmts;
@@ -191,17 +191,14 @@ static PLpgSQL_expr *make_tupret_expr(PLpgSQL_row *row);
*/
%token T_FUNCTION
%token T_TRIGGER
-%token T_LABEL
%token T_STRING
-%token T_VARIABLE
+%token T_NUMBER
+%token T_VARIABLE /* a VAR, RECFIELD, or TRIGARG */
%token T_ROW
-%token T_ROWTYPE
%token T_RECORD
-%token T_RECFIELD
-%token T_TGARGV
%token T_DTYPE
+%token T_LABEL
%token T_WORD
-%token T_NUMBER
%token T_ERROR
%token O_OPTION
@@ -514,16 +511,16 @@ decl_is_from : K_IS | /* Oracle */
decl_aliasitem : T_WORD
{
- PLpgSQL_nsitem *nsi;
char *name;
+ PLpgSQL_nsitem *nsi;
- plpgsql_ns_setlocal(false);
- name = plpgsql_tolower(yytext);
+ plpgsql_convert_ident(yytext, &name, 1);
if (name[0] != '$')
{
plpgsql_error_lineno = yylineno;
elog(ERROR, "can only alias positional parameters");
}
+ plpgsql_ns_setlocal(false);
nsi = plpgsql_ns_lookup(name, NULL);
if (nsi == NULL)
{
@@ -533,6 +530,8 @@ decl_aliasitem : T_WORD
plpgsql_ns_setlocal(true);
+ pfree(name);
+
$$ = nsi;
}
;
@@ -545,16 +544,23 @@ decl_rowtype : T_ROW
decl_varname : T_WORD
{
+ char *name;
+
+ plpgsql_convert_ident(yytext, &name, 1);
/* name should be malloc'd for use as varname */
- $$.name = strdup(plpgsql_tolower(yytext));
+ $$.name = strdup(name);
$$.lineno = yylineno;
+ pfree(name);
}
;
decl_renname : T_WORD
{
+ char *name;
+
+ plpgsql_convert_ident(yytext, &name, 1);
/* the result must be palloc'd, see plpgsql_ns_rename */
- $$ = plpgsql_tolower(yytext);
+ $$ = name;
}
;
@@ -808,32 +814,16 @@ getdiag_item : K_ROW_COUNT
getdiag_target : T_VARIABLE
{
- if (yylval.var->isconst)
- {
- plpgsql_error_lineno = yylineno;
- elog(ERROR, "%s is declared CONSTANT; can not receive diagnostics", yylval.var->refname);
- }
- $$ = yylval.var->varno;
- }
- | T_RECFIELD
- {
- $$ = yylval.recfield->rfno;
+ check_assignable(yylval.variable);
+ $$ = yylval.variable->dno;
}
;
assign_var : T_VARIABLE
{
- if (yylval.var->isconst)
- {
- plpgsql_error_lineno = yylineno;
- elog(ERROR, "%s is declared CONSTANT", yylval.var->refname);
- }
- $$ = yylval.var->varno;
- }
- | T_RECFIELD
- {
- $$ = yylval.recfield->rfno;
+ check_assignable(yylval.variable);
+ $$ = yylval.variable->dno;
}
;
@@ -998,13 +988,23 @@ fori_var : fori_varname
fori_varname : T_VARIABLE
{
- $$.name = strdup(yytext);
- $$.lineno = yylineno;
+ char *name;
+
+ plpgsql_convert_ident(yytext, &name, 1);
+ /* name should be malloc'd for use as varname */
+ $$.name = strdup(name);
+ $$.lineno = yylineno;
+ pfree(name);
}
| T_WORD
{
- $$.name = strdup(yytext);
- $$.lineno = yylineno;
+ char *name;
+
+ plpgsql_convert_ident(yytext, &name, 1);
+ /* name should be malloc'd for use as varname */
+ $$.name = strdup(name);
+ $$.lineno = yylineno;
+ pfree(name);
}
;
@@ -1254,15 +1254,7 @@ raise_params : raise_params raise_param
raise_param : ',' T_VARIABLE
{
- $$ = yylval.var->varno;
- }
- | ',' T_RECFIELD
- {
- $$ = yylval.recfield->rfno;
- }
- | ',' T_TGARGV
- {
- $$ = yylval.trigarg->dno;
+ $$ = yylval.variable->dno;
}
;
@@ -1440,23 +1432,35 @@ stmt_close : K_CLOSE lno cursor_variable ';'
cursor_varptr : T_VARIABLE
{
- if (yylval.var->datatype->typoid != REFCURSOROID)
+ if (yylval.variable->dtype != PLPGSQL_DTYPE_VAR)
+ {
+ plpgsql_error_lineno = yylineno;
+ elog(ERROR, "cursor variable must be a simple variable");
+ }
+ if (((PLpgSQL_var *) yylval.variable)->datatype->typoid != REFCURSOROID)
{
plpgsql_error_lineno = yylineno;
- elog(ERROR, "%s must be of type cursor or refcursor", yylval.var->refname);
+ elog(ERROR, "%s must be of type cursor or refcursor",
+ ((PLpgSQL_var *) yylval.variable)->refname);
}
- $$ = yylval.var;
+ $$ = (PLpgSQL_var *) yylval.variable;
}
;
cursor_variable : T_VARIABLE
{
- if (yylval.var->datatype->typoid != REFCURSOROID)
+ if (yylval.variable->dtype != PLPGSQL_DTYPE_VAR)
+ {
+ plpgsql_error_lineno = yylineno;
+ elog(ERROR, "cursor variable must be a simple variable");
+ }
+ if (((PLpgSQL_var *) yylval.variable)->datatype->typoid != REFCURSOROID)
{
plpgsql_error_lineno = yylineno;
- elog(ERROR, "%s must be of type refcursor", yylval.var->refname);
+ elog(ERROR, "%s must be of type refcursor",
+ ((PLpgSQL_var *) yylval.variable)->refname);
}
- $$ = yylval.var->varno;
+ $$ = yylval.variable->dno;
}
;
@@ -1503,7 +1507,13 @@ opt_exitcond : ';'
;
opt_lblname : T_WORD
- { $$ = strdup(yytext); }
+ {
+ char *name;
+
+ plpgsql_convert_ident(yytext, &name, 1);
+ $$ = strdup(name);
+ pfree(name);
+ }
;
lno :
@@ -1583,19 +1593,7 @@ read_sql_construct(int until,
switch (tok)
{
case T_VARIABLE:
- params[nparams] = yylval.var->varno;
- sprintf(buf, " $%d ", ++nparams);
- plpgsql_dstring_append(&ds, buf);
- break;
-
- case T_RECFIELD:
- params[nparams] = yylval.recfield->rfno;
- sprintf(buf, " $%d ", ++nparams);
- plpgsql_dstring_append(&ds, buf);
- break;
-
- case T_TGARGV:
- params[nparams] = yylval.trigarg->dno;
+ params[nparams] = yylval.variable->dno;
sprintf(buf, " $%d ", ++nparams);
plpgsql_dstring_append(&ds, buf);
break;
@@ -1681,10 +1679,8 @@ read_datatype(int tok)
static PLpgSQL_stmt *
-make_select_stmt()
+make_select_stmt(void)
{
- int tok;
- int lno;
PLpgSQL_dstring ds;
int nparams = 0;
int params[1024];
@@ -1692,220 +1688,101 @@ make_select_stmt()
PLpgSQL_expr *expr;
PLpgSQL_row *row = NULL;
PLpgSQL_rec *rec = NULL;
- PLpgSQL_stmt_select *select;
+ int tok = 0;
int have_nexttok = 0;
+ int have_into = 0;
- lno = yylineno;
plpgsql_dstring_init(&ds);
plpgsql_dstring_append(&ds, "SELECT ");
- while((tok = yylex()) != K_INTO)
+ while(1)
{
+ if (!have_nexttok)
+ tok = yylex();
+ have_nexttok = 0;
if (tok == ';')
+ break;
+ if (tok == 0)
{
- PLpgSQL_stmt_execsql *execsql;
-
- expr = malloc(sizeof(PLpgSQL_expr) + sizeof(int) * nparams - sizeof(int));
- expr->dtype = PLPGSQL_DTYPE_EXPR;
- expr->query = strdup(plpgsql_dstring_get(&ds));
- expr->plan = NULL;
- expr->nparams = nparams;
- while(nparams-- > 0)
- expr->params[nparams] = params[nparams];
- plpgsql_dstring_free(&ds);
-
- execsql = malloc(sizeof(PLpgSQL_stmt_execsql));
- execsql->cmd_type = PLPGSQL_STMT_EXECSQL;
- execsql->sqlstmt = expr;
-
- return (PLpgSQL_stmt *)execsql;
+ plpgsql_error_lineno = yylineno;
+ elog(ERROR, "unexpected end of file");
}
-
- if (plpgsql_SpaceScanned)
- plpgsql_dstring_append(&ds, " ");
- switch (tok)
+ if (tok == K_INTO)
{
- case T_VARIABLE:
- params[nparams] = yylval.var->varno;
- sprintf(buf, " $%d ", ++nparams);
- plpgsql_dstring_append(&ds, buf);
- break;
-
- case T_RECFIELD:
- params[nparams] = yylval.recfield->rfno;
- sprintf(buf, " $%d ", ++nparams);
- plpgsql_dstring_append(&ds, buf);
- break;
-
- case T_TGARGV:
- params[nparams] = yylval.trigarg->dno;
- sprintf(buf, " $%d ", ++nparams);
- plpgsql_dstring_append(&ds, buf);
- break;
-
- default:
- if (tok == 0)
- {
- plpgsql_error_lineno = yylineno;
- elog(ERROR, "unexpected end of file");
- }
- plpgsql_dstring_append(&ds, yytext);
- break;
- }
- }
-
- tok = yylex();
- switch (tok)
- {
- case T_ROW:
- row = yylval.row;
- break;
-
- case T_RECORD:
- rec = yylval.rec;
- break;
-
- case T_VARIABLE:
- case T_RECFIELD:
+ if (have_into)
{
- PLpgSQL_var *var;
- PLpgSQL_recfield *recfield;
- int nfields = 1;
- char *fieldnames[1024];
- int varnos[1024];
-
- switch (tok)
- {
- case T_VARIABLE:
- var = yylval.var;
- fieldnames[0] = strdup(yytext);
- varnos[0] = var->varno;
- break;
-
- case T_RECFIELD:
- recfield = yylval.recfield;
- fieldnames[0] = strdup(yytext);
- varnos[0] = recfield->rfno;
- break;
- }
-
- while ((tok = yylex()) == ',')
- {
- tok = yylex();
- switch(tok)
- {
- case T_VARIABLE:
- var = yylval.var;
- fieldnames[nfields] = strdup(yytext);
- varnos[nfields++] = var->varno;
- break;
-
- case T_RECFIELD:
- recfield = yylval.recfield;
- fieldnames[0] = strdup(yytext);
- varnos[0] = recfield->rfno;
- break;
-
- default:
- plpgsql_error_lineno = yylineno;
- elog(ERROR, "plpgsql: %s is not a variable or record field", yytext);
- }
- }
- row = malloc(sizeof(PLpgSQL_row));
- row->dtype = PLPGSQL_DTYPE_ROW;
- row->refname = strdup("*internal*");
- row->lineno = yylineno;
- row->rowtypeclass = InvalidOid;
- row->nfields = nfields;
- row->fieldnames = malloc(sizeof(char *) * nfields);
- row->varnos = malloc(sizeof(int) * nfields);
- while (--nfields >= 0)
- {
- row->fieldnames[nfields] = fieldnames[nfields];
- row->varnos[nfields] = varnos[nfields];
- }
-
- plpgsql_adddatum((PLpgSQL_datum *)row);
-
- have_nexttok = 1;
+ plpgsql_error_lineno = yylineno;
+ elog(ERROR, "INTO specified more than once");
}
- break;
-
- default:
+ tok = yylex();
+ switch (tok)
{
- if (plpgsql_SpaceScanned)
- plpgsql_dstring_append(&ds, " ");
- plpgsql_dstring_append(&ds, yytext);
+ case T_ROW:
+ row = yylval.row;
+ have_into = 1;
+ break;
- while(1)
+ case T_RECORD:
+ rec = yylval.rec;
+ have_into = 1;
+ break;
+
+ case T_VARIABLE:
{
- tok = yylex();
- if (tok == ';')
- {
- PLpgSQL_stmt_execsql *execsql;
+ int nfields = 1;
+ char *fieldnames[1024];
+ int varnos[1024];
- expr = malloc(sizeof(PLpgSQL_expr) + sizeof(int) * nparams - sizeof(int));
- expr->dtype = PLPGSQL_DTYPE_EXPR;
- expr->query = strdup(plpgsql_dstring_get(&ds));
- expr->plan = NULL;
- expr->nparams = nparams;
- while (nparams-- > 0)
- expr->params[nparams] = params[nparams];
- plpgsql_dstring_free(&ds);
+ check_assignable(yylval.variable);
+ fieldnames[0] = strdup(yytext);
+ varnos[0] = yylval.variable->dno;
- execsql = malloc(sizeof(PLpgSQL_stmt_execsql));
- execsql->cmd_type = PLPGSQL_STMT_EXECSQL;
- execsql->sqlstmt = expr;
+ while ((tok = yylex()) == ',')
+ {
+ tok = yylex();
+ switch(tok)
+ {
+ case T_VARIABLE:
+ check_assignable(yylval.variable);
+ fieldnames[nfields] = strdup(yytext);
+ varnos[nfields++] = yylval.variable->dno;
+ break;
- return (PLpgSQL_stmt *)execsql;
+ default:
+ plpgsql_error_lineno = yylineno;
+ elog(ERROR, "plpgsql: %s is not a variable",
+ yytext);
+ }
}
+ have_nexttok = 1;
- if (plpgsql_SpaceScanned)
- plpgsql_dstring_append(&ds, " ");
- switch (tok)
+ row = malloc(sizeof(PLpgSQL_row));
+ row->dtype = PLPGSQL_DTYPE_ROW;
+ row->refname = strdup("*internal*");
+ row->lineno = yylineno;
+ row->rowtypeclass = InvalidOid;
+ row->nfields = nfields;
+ row->fieldnames = malloc(sizeof(char *) * nfields);
+ row->varnos = malloc(sizeof(int) * nfields);
+ while (--nfields >= 0)
{
- case T_VARIABLE:
- params[nparams] = yylval.var->varno;
- sprintf(buf, " $%d ", ++nparams);
- plpgsql_dstring_append(&ds, buf);
- break;
-
- case T_RECFIELD:
- params[nparams] = yylval.recfield->rfno;
- sprintf(buf, " $%d ", ++nparams);
- plpgsql_dstring_append(&ds, buf);
- break;
+ row->fieldnames[nfields] = fieldnames[nfields];
+ row->varnos[nfields] = varnos[nfields];
+ }
- case T_TGARGV:
- params[nparams] = yylval.trigarg->dno;
- sprintf(buf, " $%d ", ++nparams);
- plpgsql_dstring_append(&ds, buf);
- break;
+ plpgsql_adddatum((PLpgSQL_datum *)row);
- default:
- if (tok == 0)
- {
- plpgsql_error_lineno = yylineno;
- elog(ERROR, "unexpected end of file");
- }
- plpgsql_dstring_append(&ds, yytext);
- break;
- }
+ have_into = 1;
}
- }
- }
+ break;
- /************************************************************
- * Eat up the rest of the statement after the target fields
- ************************************************************/
- while(1)
- {
- if (!have_nexttok) {
- tok = yylex();
- }
- have_nexttok = 0;
- if (tok == ';') {
- break;
+ default:
+ /* Treat the INTO as non-special */
+ plpgsql_dstring_append(&ds, " INTO ");
+ have_nexttok = 1;
+ break;
+ }
+ continue;
}
if (plpgsql_SpaceScanned)
@@ -1913,29 +1790,12 @@ make_select_stmt()
switch (tok)
{
case T_VARIABLE:
- params[nparams] = yylval.var->varno;
- sprintf(buf, " $%d ", ++nparams);
- plpgsql_dstring_append(&ds, buf);
- break;
-
- case T_RECFIELD:
- params[nparams] = yylval.recfield->rfno;
- sprintf(buf, " $%d ", ++nparams);
- plpgsql_dstring_append(&ds, buf);
- break;
-
- case T_TGARGV:
- params[nparams] = yylval.trigarg->dno;
+ params[nparams] = yylval.variable->dno;
sprintf(buf, " $%d ", ++nparams);
plpgsql_dstring_append(&ds, buf);
break;
default:
- if (tok == 0)
- {
- plpgsql_error_lineno = yylineno;
- elog(ERROR, "unexpected end of file");
- }
plpgsql_dstring_append(&ds, yytext);
break;
}
@@ -1950,19 +1810,34 @@ make_select_stmt()
expr->params[nparams] = params[nparams];
plpgsql_dstring_free(&ds);
- select = malloc(sizeof(PLpgSQL_stmt_select));
- memset(select, 0, sizeof(PLpgSQL_stmt_select));
- select->cmd_type = PLPGSQL_STMT_SELECT;
- select->rec = rec;
- select->row = row;
- select->query = expr;
+ if (have_into)
+ {
+ PLpgSQL_stmt_select *select;
+
+ select = malloc(sizeof(PLpgSQL_stmt_select));
+ memset(select, 0, sizeof(PLpgSQL_stmt_select));
+ select->cmd_type = PLPGSQL_STMT_SELECT;
+ select->rec = rec;
+ select->row = row;
+ select->query = expr;
- return (PLpgSQL_stmt *)select;
+ return (PLpgSQL_stmt *)select;
+ }
+ else
+ {
+ PLpgSQL_stmt_execsql *execsql;
+
+ execsql = malloc(sizeof(PLpgSQL_stmt_execsql));
+ execsql->cmd_type = PLPGSQL_STMT_EXECSQL;
+ execsql->sqlstmt = expr;
+
+ return (PLpgSQL_stmt *)execsql;
+ }
}
static PLpgSQL_stmt *
-make_fetch_stmt()
+make_fetch_stmt(void)
{
int tok;
PLpgSQL_row *row = NULL;
@@ -1970,6 +1845,8 @@ make_fetch_stmt()
PLpgSQL_stmt_fetch *fetch;
int have_nexttok = 0;
+ /* We have already parsed everything through the INTO keyword */
+
tok = yylex();
switch (tok)
{
@@ -1982,28 +1859,14 @@ make_fetch_stmt()
break;
case T_VARIABLE:
- case T_RECFIELD:
{
- PLpgSQL_var *var;
- PLpgSQL_recfield *recfield;
int nfields = 1;
char *fieldnames[1024];
int varnos[1024];
- switch (tok)
- {
- case T_VARIABLE:
- var = yylval.var;
- fieldnames[0] = strdup(yytext);
- varnos[0] = var->varno;
- break;
-
- case T_RECFIELD:
- recfield = yylval.recfield;
- fieldnames[0] = strdup(yytext);
- varnos[0] = recfield->rfno;
- break;
- }
+ check_assignable(yylval.variable);
+ fieldnames[0] = strdup(yytext);
+ varnos[0] = yylval.variable->dno;
while ((tok = yylex()) == ',')
{
@@ -2011,22 +1874,19 @@ make_fetch_stmt()
switch(tok)
{
case T_VARIABLE:
- var = yylval.var;
+ check_assignable(yylval.variable);
fieldnames[nfields] = strdup(yytext);
- varnos[nfields++] = var->varno;
- break;
-
- case T_RECFIELD:
- recfield = yylval.recfield;
- fieldnames[0] = strdup(yytext);
- varnos[0] = recfield->rfno;
+ varnos[nfields++] = yylval.variable->dno;
break;
default:
plpgsql_error_lineno = yylineno;
- elog(ERROR, "plpgsql: %s is not a variable or record field", yytext);
+ elog(ERROR, "plpgsql: %s is not a variable",
+ yytext);
}
}
+ have_nexttok = 1;
+
row = malloc(sizeof(PLpgSQL_row));
row->dtype = PLPGSQL_DTYPE_ROW;
row->refname = strdup("*internal*");
@@ -2042,8 +1902,6 @@ make_fetch_stmt()
}
plpgsql_adddatum((PLpgSQL_datum *)row);
-
- have_nexttok = 1;
}
break;
@@ -2100,3 +1958,29 @@ make_tupret_expr(PLpgSQL_row *row)
plpgsql_dstring_free(&ds);
return expr;
}
+
+static void
+check_assignable(PLpgSQL_datum *datum)
+{
+ switch (datum->dtype)
+ {
+ case PLPGSQL_DTYPE_VAR:
+ if (((PLpgSQL_var *) datum)->isconst)
+ {
+ plpgsql_error_lineno = yylineno;
+ elog(ERROR, "%s is declared CONSTANT",
+ ((PLpgSQL_var *) datum)->refname);
+ }
+ break;
+ case PLPGSQL_DTYPE_RECFIELD:
+ /* always assignable? */
+ break;
+ case PLPGSQL_DTYPE_TRIGARG:
+ plpgsql_error_lineno = yylineno;
+ elog(ERROR, "cannot assign to tg_argv");
+ break;
+ default:
+ elog(ERROR, "check_assignable: unexpected datum type");
+ break;
+ }
+}
diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c
index bc95eba1ed1..d3c7cb0ef02 100644
--- a/src/pl/plpgsql/src/pl_comp.c
+++ b/src/pl/plpgsql/src/pl_comp.c
@@ -3,7 +3,7 @@
* procedural language
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.43 2002/08/02 18:15:09 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.44 2002/08/08 01:36:04 tgl Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
@@ -269,7 +269,8 @@ plpgsql_compile(Oid fn_oid, int functype)
row->refname = strdup(buf);
plpgsql_adddatum((PLpgSQL_datum *) row);
- plpgsql_ns_additem(PLPGSQL_NSTYPE_ROW, row->rowno, buf);
+ plpgsql_ns_additem(PLPGSQL_NSTYPE_ROW, row->rowno,
+ row->refname);
arg_varnos[i] = row->rowno;
}
@@ -299,7 +300,8 @@ plpgsql_compile(Oid fn_oid, int functype)
var->default_val = NULL;
plpgsql_adddatum((PLpgSQL_datum *) var);
- plpgsql_ns_additem(PLPGSQL_NSTYPE_VAR, var->varno, buf);
+ plpgsql_ns_additem(PLPGSQL_NSTYPE_VAR, var->varno,
+ var->refname);
arg_varnos[i] = var->varno;
}
@@ -495,7 +497,7 @@ plpgsql_compile(Oid fn_oid, int functype)
var->default_val = NULL;
plpgsql_adddatum((PLpgSQL_datum *) var);
- plpgsql_ns_additem(PLPGSQL_NSTYPE_VAR, var->varno, strdup("found"));
+ plpgsql_ns_additem(PLPGSQL_NSTYPE_VAR, var->varno, var->refname);
function->found_varno = var->varno;
/*
@@ -550,19 +552,17 @@ int
plpgsql_parse_word(char *word)
{
PLpgSQL_nsitem *nse;
- char *cp;
+ char *cp[1];
- /*
- * We do our lookups case insensitive
- */
- cp = plpgsql_tolower(word);
+ /* Do case conversion and word separation */
+ plpgsql_convert_ident(word, cp, 1);
/*
- * Special handling when compiling triggers
+ * Recognize tg_argv when compiling triggers
*/
if (plpgsql_curr_compile->fn_functype == T_TRIGGER)
{
- if (strcmp(cp, "tg_argv") == 0)
+ if (strcmp(cp[0], "tg_argv") == 0)
{
int save_spacescanned = plpgsql_SpaceScanned;
PLpgSQL_trigarg *trigarg;
@@ -577,20 +577,21 @@ plpgsql_parse_word(char *word)
trigarg->argnum = plpgsql_read_expression(']', "]");
plpgsql_adddatum((PLpgSQL_datum *) trigarg);
- plpgsql_yylval.trigarg = trigarg;
+ plpgsql_yylval.variable = (PLpgSQL_datum *) trigarg;
plpgsql_SpaceScanned = save_spacescanned;
- return T_TGARGV;
+ pfree(cp[0]);
+ return T_VARIABLE;
}
}
/*
* Do a lookup on the compilers namestack
*/
- nse = plpgsql_ns_lookup(cp, NULL);
+ nse = plpgsql_ns_lookup(cp[0], NULL);
if (nse != NULL)
{
- pfree(cp);
+ pfree(cp[0]);
switch (nse->itemtype)
{
case PLPGSQL_NSTYPE_LABEL:
@@ -617,7 +618,7 @@ plpgsql_parse_word(char *word)
* Nothing found - up to now it's a word without any special meaning
* for us.
*/
- pfree(cp);
+ pfree(cp[0]);
return T_WORD;
}
@@ -628,26 +629,22 @@ plpgsql_parse_word(char *word)
* ----------
*/
int
-plpgsql_parse_dblword(char *string)
+plpgsql_parse_dblword(char *word)
{
- char *word1;
- char *word2;
PLpgSQL_nsitem *ns;
+ char *cp[2];
- /*
- * Convert to lower case and separate the words
- */
- word1 = plpgsql_tolower(string);
- word2 = strchr(word1, '.');
- *word2++ = '\0';
+ /* Do case conversion and word separation */
+ plpgsql_convert_ident(word, cp, 2);
/*
* Lookup the first word
*/
- ns = plpgsql_ns_lookup(word1, NULL);
+ ns = plpgsql_ns_lookup(cp[0], NULL);
if (ns == NULL)
{
- pfree(word1);
+ pfree(cp[0]);
+ pfree(cp[1]);
return T_ERROR;
}
@@ -661,33 +658,29 @@ plpgsql_parse_dblword(char *string)
* only be something in a query given to the SPI manager and
* T_ERROR will get eaten up by the collector routines.
*/
- ns = plpgsql_ns_lookup(word2, word1);
+ ns = plpgsql_ns_lookup(cp[1], cp[0]);
+ pfree(cp[0]);
+ pfree(cp[1]);
if (ns == NULL)
- {
- pfree(word1);
return T_ERROR;
- }
switch (ns->itemtype)
{
case PLPGSQL_NSTYPE_VAR:
plpgsql_yylval.var = (PLpgSQL_var *) (plpgsql_Datums[ns->itemno]);
- pfree(word1);
return T_VARIABLE;
case PLPGSQL_NSTYPE_REC:
plpgsql_yylval.rec = (PLpgSQL_rec *) (plpgsql_Datums[ns->itemno]);
- pfree(word1);
return T_RECORD;
case PLPGSQL_NSTYPE_ROW:
plpgsql_yylval.row = (PLpgSQL_row *) (plpgsql_Datums[ns->itemno]);
- pfree(word1);
return T_ROW;
default:
- pfree(word1);
return T_ERROR;
}
+ break;
case PLPGSQL_NSTYPE_REC:
{
@@ -699,14 +692,16 @@ plpgsql_parse_dblword(char *string)
new = malloc(sizeof(PLpgSQL_recfield));
new->dtype = PLPGSQL_DTYPE_RECFIELD;
- new->fieldname = strdup(word2);
+ new->fieldname = strdup(cp[1]);
new->recno = ns->itemno;
plpgsql_adddatum((PLpgSQL_datum *) new);
- pfree(word1);
- plpgsql_yylval.recfield = new;
- return T_RECFIELD;
+ plpgsql_yylval.variable = (PLpgSQL_datum *) new;
+
+ pfree(cp[0]);
+ pfree(cp[1]);
+ return T_VARIABLE;
}
case PLPGSQL_NSTYPE_ROW:
@@ -721,22 +716,24 @@ plpgsql_parse_dblword(char *string)
row = (PLpgSQL_row *) (plpgsql_Datums[ns->itemno]);
for (i = 0; i < row->nfields; i++)
{
- if (strcmp(row->fieldnames[i], word2) == 0)
+ if (strcmp(row->fieldnames[i], cp[1]) == 0)
{
plpgsql_yylval.var = (PLpgSQL_var *) (plpgsql_Datums[row->varnos[i]]);
- pfree(word1);
+ pfree(cp[0]);
+ pfree(cp[1]);
return T_VARIABLE;
}
}
elog(ERROR, "row %s doesn't have a field %s",
- word1, word2);
+ cp[0], cp[1]);
}
default:
break;
}
- pfree(word1);
+ pfree(cp[0]);
+ pfree(cp[1]);
return T_ERROR;
}
@@ -747,44 +744,42 @@ plpgsql_parse_dblword(char *string)
* ----------
*/
int
-plpgsql_parse_tripword(char *string)
+plpgsql_parse_tripword(char *word)
{
- char *word1;
- char *word2;
- char *word3;
PLpgSQL_nsitem *ns;
+ char *cp[3];
- /*
- * Convert to lower case and separate the words
- */
- word1 = plpgsql_tolower(string);
- word2 = strchr(word1, '.');
- *word2++ = '\0';
- word3 = strchr(word2, '.');
- *word3++ = '\0';
+ /* Do case conversion and word separation */
+ plpgsql_convert_ident(word, cp, 3);
/*
* Lookup the first word - it must be a label
*/
- ns = plpgsql_ns_lookup(word1, NULL);
+ ns = plpgsql_ns_lookup(cp[0], NULL);
if (ns == NULL)
{
- pfree(word1);
+ pfree(cp[0]);
+ pfree(cp[1]);
+ pfree(cp[2]);
return T_ERROR;
}
if (ns->itemtype != PLPGSQL_NSTYPE_LABEL)
{
- pfree(word1);
+ pfree(cp[0]);
+ pfree(cp[1]);
+ pfree(cp[2]);
return T_ERROR;
}
/*
* First word is a label, so second word could be a record or row
*/
- ns = plpgsql_ns_lookup(word2, word1);
+ ns = plpgsql_ns_lookup(cp[1], cp[0]);
if (ns == NULL)
{
- pfree(word1);
+ pfree(cp[0]);
+ pfree(cp[1]);
+ pfree(cp[2]);
return T_ERROR;
}
@@ -800,14 +795,17 @@ plpgsql_parse_tripword(char *string)
new = malloc(sizeof(PLpgSQL_recfield));
new->dtype = PLPGSQL_DTYPE_RECFIELD;
- new->fieldname = strdup(word3);
+ new->fieldname = strdup(cp[2]);
new->recno = ns->itemno;
plpgsql_adddatum((PLpgSQL_datum *) new);
- pfree(word1);
- plpgsql_yylval.recfield = new;
- return T_RECFIELD;
+ plpgsql_yylval.variable = (PLpgSQL_datum *) new;
+
+ pfree(cp[0]);
+ pfree(cp[1]);
+ pfree(cp[2]);
+ return T_VARIABLE;
}
case PLPGSQL_NSTYPE_ROW:
@@ -822,22 +820,26 @@ plpgsql_parse_tripword(char *string)
row = (PLpgSQL_row *) (plpgsql_Datums[ns->itemno]);
for (i = 0; i < row->nfields; i++)
{
- if (strcmp(row->fieldnames[i], word3) == 0)
+ if (strcmp(row->fieldnames[i], cp[2]) == 0)
{
plpgsql_yylval.var = (PLpgSQL_var *) (plpgsql_Datums[row->varnos[i]]);
- pfree(word1);
+ pfree(cp[0]);
+ pfree(cp[1]);
+ pfree(cp[2]);
return T_VARIABLE;
}
}
elog(ERROR, "row %s.%s doesn't have a field %s",
- word1, word2, word3);
+ cp[0], cp[1], cp[2]);
}
default:
break;
}
- pfree(word1);
+ pfree(cp[0]);
+ pfree(cp[1]);
+ pfree(cp[2]);
return T_ERROR;
}
@@ -851,27 +853,31 @@ int
plpgsql_parse_wordtype(char *word)
{
PLpgSQL_nsitem *nse;
- char *cp;
bool old_nsstate;
Oid typeOid;
+ char *cp[2];
+ int i;
- /*
- * We do our lookups case insensitive
- */
- cp = plpgsql_tolower(word);
- *(strchr(cp, '%')) = '\0';
+ /* Do case conversion and word separation */
+ /* We convert %type to .type momentarily to keep converter happy */
+ i = strlen(word) - 5;
+ Assert(word[i] == '%');
+ word[i] = '.';
+ plpgsql_convert_ident(word, cp, 2);
+ word[i] = '%';
+ pfree(cp[1]);
/*
* Do a lookup on the compilers namestack. But ensure it moves up to
* the toplevel.
*/
old_nsstate = plpgsql_ns_setlocal(false);
- nse = plpgsql_ns_lookup(cp, NULL);
+ nse = plpgsql_ns_lookup(cp[0], NULL);
plpgsql_ns_setlocal(old_nsstate);
if (nse != NULL)
{
- pfree(cp);
+ pfree(cp[0]);
switch (nse->itemtype)
{
case PLPGSQL_NSTYPE_VAR:
@@ -886,10 +892,8 @@ plpgsql_parse_wordtype(char *word)
/*
* Word wasn't found on the namestack. Try to find a data type with
* that name, but ignore pg_type entries that are in fact class types.
- *
- * XXX this should be improved to handle qualified-type-name references.
*/
- typeOid = LookupTypeName(makeTypeName(cp));
+ typeOid = LookupTypeName(makeTypeName(cp[0]));
if (OidIsValid(typeOid))
{
HeapTuple typeTup;
@@ -906,7 +910,7 @@ plpgsql_parse_wordtype(char *word)
typeStruct->typrelid != InvalidOid)
{
ReleaseSysCache(typeTup);
- pfree(cp);
+ pfree(cp[0]);
return T_ERROR;
}
@@ -923,7 +927,7 @@ plpgsql_parse_wordtype(char *word)
plpgsql_yylval.dtype = typ;
ReleaseSysCache(typeTup);
- pfree(cp);
+ pfree(cp[0]);
return T_DTYPE;
}
}
@@ -932,7 +936,7 @@ plpgsql_parse_wordtype(char *word)
* Nothing found - up to now it's a word without any special meaning
* for us.
*/
- pfree(cp);
+ pfree(cp[0]);
return T_ERROR;
}
@@ -942,10 +946,8 @@ plpgsql_parse_wordtype(char *word)
* ----------
*/
int
-plpgsql_parse_dblwordtype(char *string)
+plpgsql_parse_dblwordtype(char *word)
{
- char *word1;
- char *word2;
PLpgSQL_nsitem *nse;
bool old_nsstate;
Oid classOid;
@@ -956,20 +958,22 @@ plpgsql_parse_dblwordtype(char *string)
HeapTuple typetup;
Form_pg_type typeStruct;
PLpgSQL_type *typ;
+ char *cp[3];
+ int i;
-
- /*
- * Convert to lower case and separate the words
- */
- word1 = plpgsql_tolower(string);
- word2 = strchr(word1, '.');
- *word2++ = '\0';
- *(strchr(word2, '%')) = '\0';
+ /* Do case conversion and word separation */
+ /* We convert %type to .type momentarily to keep converter happy */
+ i = strlen(word) - 5;
+ Assert(word[i] == '%');
+ word[i] = '.';
+ plpgsql_convert_ident(word, cp, 3);
+ word[i] = '%';
+ pfree(cp[2]);
/*
* Lookup the first word
*/
- nse = plpgsql_ns_lookup(word1, NULL);
+ nse = plpgsql_ns_lookup(cp[0], NULL);
/*
* If this is a label lookup the second word in that labels namestack
@@ -980,10 +984,11 @@ plpgsql_parse_dblwordtype(char *string)
if (nse->itemtype == PLPGSQL_NSTYPE_LABEL)
{
old_nsstate = plpgsql_ns_setlocal(false);
- nse = plpgsql_ns_lookup(word2, word1);
+ nse = plpgsql_ns_lookup(cp[1], cp[0]);
plpgsql_ns_setlocal(old_nsstate);
- pfree(word1);
+ pfree(cp[0]);
+ pfree(cp[1]);
if (nse != NULL)
{
@@ -999,17 +1004,19 @@ plpgsql_parse_dblwordtype(char *string)
}
return T_ERROR;
}
- pfree(word1);
+ pfree(cp[0]);
+ pfree(cp[1]);
return T_ERROR;
}
/*
* First word could also be a table name
*/
- classOid = RelnameGetRelid(word1);
+ classOid = RelnameGetRelid(cp[0]);
if (!OidIsValid(classOid))
{
- pfree(word1);
+ pfree(cp[0]);
+ pfree(cp[1]);
return T_ERROR;
}
classtup = SearchSysCache(RELOID,
@@ -1017,7 +1024,8 @@ plpgsql_parse_dblwordtype(char *string)
0, 0, 0);
if (!HeapTupleIsValid(classtup))
{
- pfree(word1);
+ pfree(cp[0]);
+ pfree(cp[1]);
return T_ERROR;
}
@@ -1030,18 +1038,20 @@ plpgsql_parse_dblwordtype(char *string)
classStruct->relkind != RELKIND_VIEW)
{
ReleaseSysCache(classtup);
- pfree(word1);
+ pfree(cp[0]);
+ pfree(cp[1]);
return T_ERROR;
}
/*
* Fetch the named table field and it's type
*/
- attrtup = SearchSysCacheAttName(classOid, word2);
+ attrtup = SearchSysCacheAttName(classOid, cp[1]);
if (!HeapTupleIsValid(attrtup))
{
ReleaseSysCache(classtup);
- pfree(word1);
+ pfree(cp[0]);
+ pfree(cp[1]);
return T_ERROR;
}
attrStruct = (Form_pg_attribute) GETSTRUCT(attrtup);
@@ -1051,7 +1061,7 @@ plpgsql_parse_dblwordtype(char *string)
0, 0, 0);
if (!HeapTupleIsValid(typetup))
elog(ERROR, "cache lookup for type %u of %s.%s failed",
- attrStruct->atttypid, word1, word2);
+ attrStruct->atttypid, cp[0], cp[1]);
typeStruct = (Form_pg_type) GETSTRUCT(typetup);
/*
@@ -1072,7 +1082,8 @@ plpgsql_parse_dblwordtype(char *string)
ReleaseSysCache(classtup);
ReleaseSysCache(attrtup);
ReleaseSysCache(typetup);
- pfree(word1);
+ pfree(cp[0]);
+ pfree(cp[1]);
return T_DTYPE;
}
@@ -1083,7 +1094,7 @@ plpgsql_parse_dblwordtype(char *string)
* ----------
*/
int
-plpgsql_parse_wordrowtype(char *string)
+plpgsql_parse_wordrowtype(char *word)
{
Oid classOid;
HeapTuple classtup;
@@ -1092,33 +1103,38 @@ plpgsql_parse_wordrowtype(char *string)
Form_pg_type typeStruct;
HeapTuple attrtup;
Form_pg_attribute attrStruct;
- char *word1;
- char *cp;
- int i;
PLpgSQL_row *row;
PLpgSQL_var *var;
+ char *attname;
+ char *cp[2];
+ int i;
+
+ /* Do case conversion and word separation */
+ /* We convert %rowtype to .rowtype momentarily to keep converter happy */
+ i = strlen(word) - 8;
+ Assert(word[i] == '%');
+ word[i] = '.';
+ plpgsql_convert_ident(word, cp, 2);
+ word[i] = '%';
+ pfree(cp[1]);
/*
- * Get the word in lower case and fetch the pg_class tuple.
+ * Fetch the pg_class tuple.
*/
- word1 = plpgsql_tolower(string);
- cp = strchr(word1, '%');
- *cp = '\0';
-
- classOid = RelnameGetRelid(word1);
+ classOid = RelnameGetRelid(cp[0]);
if (!OidIsValid(classOid))
- elog(ERROR, "%s: no such class", word1);
+ elog(ERROR, "%s: no such class", cp[0]);
classtup = SearchSysCache(RELOID,
ObjectIdGetDatum(classOid),
0, 0, 0);
if (!HeapTupleIsValid(classtup))
- elog(ERROR, "%s: no such class", word1);
+ elog(ERROR, "%s: no such class", cp[0]);
classStruct = (Form_pg_class) GETSTRUCT(classtup);
/* accept relation, sequence, or view pg_class entries */
if (classStruct->relkind != RELKIND_RELATION &&
classStruct->relkind != RELKIND_SEQUENCE &&
classStruct->relkind != RELKIND_VIEW)
- elog(ERROR, "%s isn't a table", word1);
+ elog(ERROR, "%s isn't a table", cp[0]);
/*
* Create a row datum entry and all the required variables that it
@@ -1144,17 +1160,17 @@ plpgsql_parse_wordrowtype(char *string)
0, 0);
if (!HeapTupleIsValid(attrtup))
elog(ERROR, "cache lookup for attribute %d of class %s failed",
- i + 1, word1);
+ i + 1, cp[0]);
attrStruct = (Form_pg_attribute) GETSTRUCT(attrtup);
- cp = pstrdup(NameStr(attrStruct->attname));
+ attname = pstrdup(NameStr(attrStruct->attname));
typetup = SearchSysCache(TYPEOID,
ObjectIdGetDatum(attrStruct->atttypid),
0, 0, 0);
if (!HeapTupleIsValid(typetup))
elog(ERROR, "cache lookup for type %u of %s.%s failed",
- attrStruct->atttypid, word1, cp);
+ attrStruct->atttypid, cp[0], attname);
typeStruct = (Form_pg_type) GETSTRUCT(typetup);
/*
@@ -1170,10 +1186,10 @@ plpgsql_parse_wordrowtype(char *string)
var = malloc(sizeof(PLpgSQL_var));
memset(var, 0, sizeof(PLpgSQL_var));
var->dtype = PLPGSQL_DTYPE_VAR;
- var->refname = malloc(strlen(word1) + strlen(cp) + 2);
- strcpy(var->refname, word1);
+ var->refname = malloc(strlen(cp[0]) + strlen(attname) + 2);
+ strcpy(var->refname, cp[0]);
strcat(var->refname, ".");
- strcat(var->refname, cp);
+ strcat(var->refname, attname);
var->datatype = malloc(sizeof(PLpgSQL_type));
var->datatype->typname = strdup(NameStr(typeStruct->typname));
var->datatype->typoid = attrStruct->atttypid;
@@ -1197,7 +1213,7 @@ plpgsql_parse_wordrowtype(char *string)
/*
* Add the variable to the row.
*/
- row->fieldnames[i] = strdup(cp);
+ row->fieldnames[i] = strdup(attname);
row->varnos[i] = var->varno;
}
diff --git a/src/pl/plpgsql/src/pl_funcs.c b/src/pl/plpgsql/src/pl_funcs.c
index f64604bd70f..6ca9f583357 100644
--- a/src/pl/plpgsql/src/pl_funcs.c
+++ b/src/pl/plpgsql/src/pl_funcs.c
@@ -3,7 +3,7 @@
* procedural language
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_funcs.c,v 1.18 2002/05/05 17:38:26 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_funcs.c,v 1.19 2002/08/08 01:36:05 tgl Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
@@ -46,6 +46,10 @@
#include "plpgsql.h"
#include "pl.tab.h"
+#ifdef MULTIBYTE
+#include "mb/pg_wchar.h"
+#endif
+
/* ----------
* Local variables for the namestack handling
@@ -152,6 +156,9 @@ plpgsql_ns_push(char *label)
{
PLpgSQL_ns *new;
+ if (label == NULL)
+ label = "";
+
new = palloc(sizeof(PLpgSQL_ns));
memset(new, 0, sizeof(PLpgSQL_ns));
new->upper = ns_current;
@@ -192,9 +199,7 @@ plpgsql_ns_additem(int itemtype, int itemno, char *name)
PLpgSQL_ns *ns = ns_current;
PLpgSQL_nsitem *nse;
- if (name == NULL)
- name = "";
- name = plpgsql_tolower(name);
+ Assert(name != NULL);
if (ns->items_used == ns->items_alloc)
{
@@ -322,46 +327,115 @@ plpgsql_ns_rename(char *oldname, char *newname)
/* ----------
- * plpgsql_tolower Translate a string to lower case
- * but honor "" escaping.
+ * plpgsql_convert_ident
+ *
+ * Convert a possibly-qualified identifier to internal form: handle
+ * double quotes, translate to lower case where not inside quotes,
+ * truncate to NAMEDATALEN.
+ *
+ * There may be several identifiers separated by dots and optional
+ * whitespace. Each one is converted to a separate palloc'd string.
+ * The caller passes the expected number of identifiers, as well as
+ * a char* array to hold them. It is an error if we find the wrong
+ * number of identifiers (cf grammar processing of fori_varname).
+ *
+ * NOTE: the input string has already been accepted by the flex lexer,
+ * so we don't need a heckuva lot of error checking here.
* ----------
*/
-char *
-plpgsql_tolower(char *s)
+void
+plpgsql_convert_ident(const char *s, char **output, int numidents)
{
- char *sstart = s;
- char *ret;
- char *cp;
-
- ret = palloc(strlen(s) + 1);
- cp = ret;
+ const char *sstart = s;
+ int identctr = 0;
+ /* Outer loop over identifiers */
while (*s)
{
+ char *curident;
+ char *cp;
+ int i;
+
+ /* Process current identifier */
+ curident = palloc(strlen(s) + 1); /* surely enough room */
+ cp = curident;
+
if (*s == '"')
{
+ /* Quoted identifier: copy, collapsing out doubled quotes */
s++;
while (*s)
{
if (*s == '"')
- break;
+ {
+ if (s[1] != '"')
+ break;
+ s++;
+ }
*cp++ = *s++;
}
- if (*s != '"')
- elog(ERROR, "unterminated \" in name %s", sstart);
+ if (*s != '"') /* should not happen if lexer checked */
+ elog(ERROR, "unterminated \" in name: %s", sstart);
s++;
}
else
{
- if (isupper((unsigned char) *s))
- *cp++ = tolower((unsigned char) *s++);
- else
- *cp++ = *s++;
+ /*
+ * Normal identifier: downcase, stop at dot or whitespace.
+ *
+ * Note that downcasing is locale-sensitive, following SQL99
+ * rules for identifiers. We have already decided that the
+ * item is not a PLPGSQL keyword.
+ */
+ while (*s && *s != '.' && !isspace((unsigned char) *s))
+ {
+ if (isupper((unsigned char) *s))
+ *cp++ = tolower((unsigned char) *s++);
+ else
+ *cp++ = *s++;
+ }
+ }
+
+ /* Truncate to NAMEDATALEN */
+ *cp = '\0';
+ i = cp - curident;
+
+ if (i >= NAMEDATALEN)
+ {
+ int len;
+
+#ifdef MULTIBYTE
+ len = pg_mbcliplen(curident, i, NAMEDATALEN-1);
+#else
+ len = NAMEDATALEN-1;
+#endif
+ curident[len] = '\0';
+ }
+
+ /* Pass ident to caller */
+ if (identctr < numidents)
+ output[identctr++] = curident;
+ else
+ elog(ERROR, "Qualified identifier cannot be used here: %s",
+ sstart);
+
+ /* If not done, skip whitespace, dot, whitespace */
+ if (*s)
+ {
+ while (*s && isspace((unsigned char) *s))
+ s++;
+ if (*s++ != '.')
+ elog(ERROR, "Expected dot between identifiers: %s", sstart);
+ while (*s && isspace((unsigned char) *s))
+ s++;
+ if (*s == '\0')
+ elog(ERROR, "Expected another identifier: %s", sstart);
}
}
- *cp = '\0';
- return ret;
+ if (identctr != numidents)
+ elog(ERROR, "Improperly qualified identifier: %s",
+ sstart);
}
diff --git a/src/pl/plpgsql/src/plpgsql.h b/src/pl/plpgsql/src/plpgsql.h
index 041f2dd362a..e991aa96ee7 100644
--- a/src/pl/plpgsql/src/plpgsql.h
+++ b/src/pl/plpgsql/src/plpgsql.h
@@ -3,7 +3,7 @@
* procedural language
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.24 2001/11/29 22:57:37 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.25 2002/08/08 01:36:05 tgl Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
@@ -547,11 +547,11 @@ extern PLpgSQL_function *plpgsql_curr_compile;
*/
extern PLpgSQL_function *plpgsql_compile(Oid fn_oid, int functype);
extern int plpgsql_parse_word(char *word);
-extern int plpgsql_parse_dblword(char *string);
-extern int plpgsql_parse_tripword(char *string);
-extern int plpgsql_parse_wordtype(char *string);
-extern int plpgsql_parse_dblwordtype(char *string);
-extern int plpgsql_parse_wordrowtype(char *string);
+extern int plpgsql_parse_dblword(char *word);
+extern int plpgsql_parse_tripword(char *word);
+extern int plpgsql_parse_wordtype(char *word);
+extern int plpgsql_parse_dblwordtype(char *word);
+extern int plpgsql_parse_wordrowtype(char *word);
extern PLpgSQL_type *plpgsql_parse_datatype(char *string);
extern void plpgsql_adddatum(PLpgSQL_datum * new);
extern int plpgsql_add_initdatums(int **varnos);
@@ -598,7 +598,7 @@ extern void plpgsql_ns_rename(char *oldname, char *newname);
* Other functions in pl_funcs.c
* ----------
*/
-extern char *plpgsql_tolower(char *s);
+extern void plpgsql_convert_ident(const char *s, char **output, int numidents);
extern const char *plpgsql_stmt_typename(PLpgSQL_stmt * stmt);
extern void plpgsql_dumptree(PLpgSQL_function * func);
diff --git a/src/pl/plpgsql/src/scan.l b/src/pl/plpgsql/src/scan.l
index 57b351f05aa..5f0f281ada5 100644
--- a/src/pl/plpgsql/src/scan.l
+++ b/src/pl/plpgsql/src/scan.l
@@ -4,7 +4,7 @@
* procedural language
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/Attic/scan.l,v 1.20 2002/08/04 04:17:33 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/Attic/scan.l,v 1.21 2002/08/08 01:36:05 tgl Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
@@ -60,13 +60,21 @@ static void plpgsql_input(char *buf, int *result, int max);
%option noyywrap
%option yylineno
+%option case-insensitive
-WS [\200-\377_A-Za-z"]
-WC [\200-\377_A-Za-z0-9"]
-
%x IN_STRING IN_COMMENT
+digit [0-9]
+letter [\200-\377_A-Za-z]
+letter_or_digit [\200-\377_A-Za-z0-9]
+
+quoted_ident (\"[^\"]*\")+
+
+identifier ({letter}{letter_or_digit}*|{quoted_ident})
+
+space [ \t\n\r\f]
+
%%
/* ----------
* Local variable in scanner to remember where
@@ -154,37 +162,43 @@ dump { return O_DUMP; }
* Special word rules
* ----------
*/
-{WS}{WC}* { return plpgsql_parse_word(yytext); }
-{WS}{WC}*\.{WS}{WC}* { return plpgsql_parse_dblword(yytext); }
-{WS}{WC}*\.{WS}{WC}*\.{WS}{WC}* { return plpgsql_parse_tripword(yytext); }
-{WS}{WC}*%TYPE { return plpgsql_parse_wordtype(yytext); }
-{WS}{WC}*\.{WS}{WC}*%TYPE { return plpgsql_parse_dblwordtype(yytext); }
-{WS}{WC}*%ROWTYPE { return plpgsql_parse_wordrowtype(yytext); }
-
-\$[0-9]+ { return plpgsql_parse_word(yytext); }
-\$[0-9]+\.{WS}{WC}* { return plpgsql_parse_dblword(yytext); }
-\$[0-9]+\.{WS}{WC}*\.{WS}{WC}* { return plpgsql_parse_tripword(yytext); }
-\$[0-9]+%TYPE { return plpgsql_parse_wordtype(yytext); }
-\$[0-9]+\.{WS}{WC}*%TYPE { return plpgsql_parse_dblwordtype(yytext); }
-\$[0-9]+%ROWTYPE { return plpgsql_parse_wordrowtype(yytext); }
-
-[0-9]+ { return T_NUMBER; }
+{identifier} { return plpgsql_parse_word(yytext); }
+{identifier}{space}*\.{space}*{identifier} { return plpgsql_parse_dblword(yytext); }
+{identifier}{space}*\.{space}*{identifier}{space}*\.{space}*{identifier} { return plpgsql_parse_tripword(yytext); }
+{identifier}{space}*%TYPE { return plpgsql_parse_wordtype(yytext); }
+{identifier}{space}*\.{space}*{identifier}{space}*%TYPE { return plpgsql_parse_dblwordtype(yytext); }
+{identifier}{space}*%ROWTYPE { return plpgsql_parse_wordrowtype(yytext); }
+
+\${digit}+ { return plpgsql_parse_word(yytext); }
+\${digit}+{space}*\.{space}*{identifier} { return plpgsql_parse_dblword(yytext); }
+\${digit}+{space}*\.{space}*{identifier}{space}*\.{space}*{identifier} { return plpgsql_parse_tripword(yytext); }
+\${digit}+{space}*%TYPE { return plpgsql_parse_wordtype(yytext); }
+\${digit}+{space}*\.{space}*{identifier}{space}*%TYPE { return plpgsql_parse_dblwordtype(yytext); }
+\${digit}+{space}*%ROWTYPE { return plpgsql_parse_wordrowtype(yytext); }
+
+{digit}+ { return T_NUMBER; }
+
+\". {
+ plpgsql_error_lineno = yylineno;
+ elog(ERROR, "unterminated quoted identifier");
+ }
/* ----------
* Ignore whitespaces but remember this happened
* ----------
*/
-[ \t\r\n]+ { plpgsql_SpaceScanned = 1; }
+{space}+ { plpgsql_SpaceScanned = 1; }
/* ----------
* Eat up comments
* ----------
*/
--[^\r\n]* ;
+
\/\* { start_lineno = yylineno;
BEGIN IN_COMMENT;
}
-<IN_COMMENT>\*\/ { BEGIN INITIAL; }
+<IN_COMMENT>\*\/ { BEGIN INITIAL; plpgsql_SpaceScanned = 1; }
<IN_COMMENT>\n ;
<IN_COMMENT>. ;
<IN_COMMENT><<EOF>> {