Commit e42f8e32 authored by Tom Lane's avatar Tom Lane

Clean up plpgsql identifier handling: process quoted identifiers

correctly, truncate to NAMEDATALEN where needed, allow whitespace
around dots in qualified identifiers.  Get rid of T_RECFIELD and
T_TGARGV token categories, which weren't accomplishing anything
except to create room for sins of omission in the grammar, ie,
places that should have allowed them and didn't.  Fix a few other
bugs en passant.
parent 13e8be42
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
* procedural language * procedural language
* *
* IDENTIFICATION * 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. * This software is copyrighted by Jan Wieck - Hamburg.
* *
...@@ -48,6 +48,7 @@ static PLpgSQL_type *read_datatype(int tok); ...@@ -48,6 +48,7 @@ static PLpgSQL_type *read_datatype(int tok);
static PLpgSQL_stmt *make_select_stmt(void); static PLpgSQL_stmt *make_select_stmt(void);
static PLpgSQL_stmt *make_fetch_stmt(void); static PLpgSQL_stmt *make_fetch_stmt(void);
static PLpgSQL_expr *make_tupret_expr(PLpgSQL_row *row); 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); ...@@ -83,11 +84,10 @@ static PLpgSQL_expr *make_tupret_expr(PLpgSQL_row *row);
int *initvarnos; int *initvarnos;
} declhdr; } declhdr;
PLpgSQL_type *dtype; PLpgSQL_type *dtype;
PLpgSQL_datum *variable; /* a VAR, RECFIELD, or TRIGARG */
PLpgSQL_var *var; PLpgSQL_var *var;
PLpgSQL_row *row; PLpgSQL_row *row;
PLpgSQL_rec *rec; PLpgSQL_rec *rec;
PLpgSQL_recfield *recfield;
PLpgSQL_trigarg *trigarg;
PLpgSQL_expr *expr; PLpgSQL_expr *expr;
PLpgSQL_stmt *stmt; PLpgSQL_stmt *stmt;
PLpgSQL_stmts *stmts; PLpgSQL_stmts *stmts;
...@@ -191,17 +191,14 @@ static PLpgSQL_expr *make_tupret_expr(PLpgSQL_row *row); ...@@ -191,17 +191,14 @@ static PLpgSQL_expr *make_tupret_expr(PLpgSQL_row *row);
*/ */
%token T_FUNCTION %token T_FUNCTION
%token T_TRIGGER %token T_TRIGGER
%token T_LABEL
%token T_STRING %token T_STRING
%token T_VARIABLE %token T_NUMBER
%token T_VARIABLE /* a VAR, RECFIELD, or TRIGARG */
%token T_ROW %token T_ROW
%token T_ROWTYPE
%token T_RECORD %token T_RECORD
%token T_RECFIELD
%token T_TGARGV
%token T_DTYPE %token T_DTYPE
%token T_LABEL
%token T_WORD %token T_WORD
%token T_NUMBER
%token T_ERROR %token T_ERROR
%token O_OPTION %token O_OPTION
...@@ -514,16 +511,16 @@ decl_is_from : K_IS | /* Oracle */ ...@@ -514,16 +511,16 @@ decl_is_from : K_IS | /* Oracle */
decl_aliasitem : T_WORD decl_aliasitem : T_WORD
{ {
PLpgSQL_nsitem *nsi;
char *name; char *name;
PLpgSQL_nsitem *nsi;
plpgsql_ns_setlocal(false); plpgsql_convert_ident(yytext, &name, 1);
name = plpgsql_tolower(yytext);
if (name[0] != '$') if (name[0] != '$')
{ {
plpgsql_error_lineno = yylineno; plpgsql_error_lineno = yylineno;
elog(ERROR, "can only alias positional parameters"); elog(ERROR, "can only alias positional parameters");
} }
plpgsql_ns_setlocal(false);
nsi = plpgsql_ns_lookup(name, NULL); nsi = plpgsql_ns_lookup(name, NULL);
if (nsi == NULL) if (nsi == NULL)
{ {
...@@ -533,6 +530,8 @@ decl_aliasitem : T_WORD ...@@ -533,6 +530,8 @@ decl_aliasitem : T_WORD
plpgsql_ns_setlocal(true); plpgsql_ns_setlocal(true);
pfree(name);
$$ = nsi; $$ = nsi;
} }
; ;
...@@ -545,16 +544,23 @@ decl_rowtype : T_ROW ...@@ -545,16 +544,23 @@ decl_rowtype : T_ROW
decl_varname : T_WORD decl_varname : T_WORD
{ {
char *name;
plpgsql_convert_ident(yytext, &name, 1);
/* name should be malloc'd for use as varname */ /* name should be malloc'd for use as varname */
$$.name = strdup(plpgsql_tolower(yytext)); $$.name = strdup(name);
$$.lineno = yylineno; $$.lineno = yylineno;
pfree(name);
} }
; ;
decl_renname : T_WORD decl_renname : T_WORD
{ {
char *name;
plpgsql_convert_ident(yytext, &name, 1);
/* the result must be palloc'd, see plpgsql_ns_rename */ /* the result must be palloc'd, see plpgsql_ns_rename */
$$ = plpgsql_tolower(yytext); $$ = name;
} }
; ;
...@@ -808,32 +814,16 @@ getdiag_item : K_ROW_COUNT ...@@ -808,32 +814,16 @@ getdiag_item : K_ROW_COUNT
getdiag_target : T_VARIABLE getdiag_target : T_VARIABLE
{ {
if (yylval.var->isconst) check_assignable(yylval.variable);
{ $$ = yylval.variable->dno;
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;
} }
; ;
assign_var : T_VARIABLE assign_var : T_VARIABLE
{ {
if (yylval.var->isconst) check_assignable(yylval.variable);
{ $$ = yylval.variable->dno;
plpgsql_error_lineno = yylineno;
elog(ERROR, "%s is declared CONSTANT", yylval.var->refname);
}
$$ = yylval.var->varno;
}
| T_RECFIELD
{
$$ = yylval.recfield->rfno;
} }
; ;
...@@ -998,13 +988,23 @@ fori_var : fori_varname ...@@ -998,13 +988,23 @@ fori_var : fori_varname
fori_varname : T_VARIABLE fori_varname : T_VARIABLE
{ {
$$.name = strdup(yytext); char *name;
plpgsql_convert_ident(yytext, &name, 1);
/* name should be malloc'd for use as varname */
$$.name = strdup(name);
$$.lineno = yylineno; $$.lineno = yylineno;
pfree(name);
} }
| T_WORD | T_WORD
{ {
$$.name = strdup(yytext); char *name;
plpgsql_convert_ident(yytext, &name, 1);
/* name should be malloc'd for use as varname */
$$.name = strdup(name);
$$.lineno = yylineno; $$.lineno = yylineno;
pfree(name);
} }
; ;
...@@ -1254,15 +1254,7 @@ raise_params : raise_params raise_param ...@@ -1254,15 +1254,7 @@ raise_params : raise_params raise_param
raise_param : ',' T_VARIABLE raise_param : ',' T_VARIABLE
{ {
$$ = yylval.var->varno; $$ = yylval.variable->dno;
}
| ',' T_RECFIELD
{
$$ = yylval.recfield->rfno;
}
| ',' T_TGARGV
{
$$ = yylval.trigarg->dno;
} }
; ;
...@@ -1440,23 +1432,35 @@ stmt_close : K_CLOSE lno cursor_variable ';' ...@@ -1440,23 +1432,35 @@ stmt_close : K_CLOSE lno cursor_variable ';'
cursor_varptr : T_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; 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 cursor_variable : T_VARIABLE
{ {
if (yylval.var->datatype->typoid != REFCURSOROID) if (yylval.variable->dtype != PLPGSQL_DTYPE_VAR)
{ {
plpgsql_error_lineno = yylineno; plpgsql_error_lineno = yylineno;
elog(ERROR, "%s must be of type refcursor", yylval.var->refname); elog(ERROR, "cursor variable must be a simple variable");
} }
$$ = yylval.var->varno; if (((PLpgSQL_var *) yylval.variable)->datatype->typoid != REFCURSOROID)
{
plpgsql_error_lineno = yylineno;
elog(ERROR, "%s must be of type refcursor",
((PLpgSQL_var *) yylval.variable)->refname);
}
$$ = yylval.variable->dno;
} }
; ;
...@@ -1503,7 +1507,13 @@ opt_exitcond : ';' ...@@ -1503,7 +1507,13 @@ opt_exitcond : ';'
; ;
opt_lblname : T_WORD opt_lblname : T_WORD
{ $$ = strdup(yytext); } {
char *name;
plpgsql_convert_ident(yytext, &name, 1);
$$ = strdup(name);
pfree(name);
}
; ;
lno : lno :
...@@ -1583,19 +1593,7 @@ read_sql_construct(int until, ...@@ -1583,19 +1593,7 @@ read_sql_construct(int until,
switch (tok) switch (tok)
{ {
case T_VARIABLE: case T_VARIABLE:
params[nparams] = yylval.var->varno; params[nparams] = yylval.variable->dno;
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); sprintf(buf, " $%d ", ++nparams);
plpgsql_dstring_append(&ds, buf); plpgsql_dstring_append(&ds, buf);
break; break;
...@@ -1681,10 +1679,8 @@ read_datatype(int tok) ...@@ -1681,10 +1679,8 @@ read_datatype(int tok)
static PLpgSQL_stmt * static PLpgSQL_stmt *
make_select_stmt() make_select_stmt(void)
{ {
int tok;
int lno;
PLpgSQL_dstring ds; PLpgSQL_dstring ds;
int nparams = 0; int nparams = 0;
int params[1024]; int params[1024];
...@@ -1692,102 +1688,54 @@ make_select_stmt() ...@@ -1692,102 +1688,54 @@ make_select_stmt()
PLpgSQL_expr *expr; PLpgSQL_expr *expr;
PLpgSQL_row *row = NULL; PLpgSQL_row *row = NULL;
PLpgSQL_rec *rec = NULL; PLpgSQL_rec *rec = NULL;
PLpgSQL_stmt_select *select; int tok = 0;
int have_nexttok = 0; int have_nexttok = 0;
int have_into = 0;
lno = yylineno;
plpgsql_dstring_init(&ds); plpgsql_dstring_init(&ds);
plpgsql_dstring_append(&ds, "SELECT "); plpgsql_dstring_append(&ds, "SELECT ");
while((tok = yylex()) != K_INTO) while(1)
{ {
if (!have_nexttok)
tok = yylex();
have_nexttok = 0;
if (tok == ';') if (tok == ';')
{
PLpgSQL_stmt_execsql *execsql;
expr = malloc(sizeof(PLpgSQL_expr) + sizeof(int) * nparams - sizeof(int));
expr->dtype = PLPGSQL_DTYPE_EXPR;
expr->query = strdup(plpgsql_dstring_get(&ds));
expr->plan = NULL;
expr->nparams = nparams;
while(nparams-- > 0)
expr->params[nparams] = params[nparams];
plpgsql_dstring_free(&ds);
execsql = malloc(sizeof(PLpgSQL_stmt_execsql));
execsql->cmd_type = PLPGSQL_STMT_EXECSQL;
execsql->sqlstmt = expr;
return (PLpgSQL_stmt *)execsql;
}
if (plpgsql_SpaceScanned)
plpgsql_dstring_append(&ds, " ");
switch (tok)
{
case T_VARIABLE:
params[nparams] = yylval.var->varno;
sprintf(buf, " $%d ", ++nparams);
plpgsql_dstring_append(&ds, buf);
break;
case T_RECFIELD:
params[nparams] = yylval.recfield->rfno;
sprintf(buf, " $%d ", ++nparams);
plpgsql_dstring_append(&ds, buf);
break; break;
case T_TGARGV:
params[nparams] = yylval.trigarg->dno;
sprintf(buf, " $%d ", ++nparams);
plpgsql_dstring_append(&ds, buf);
break;
default:
if (tok == 0) if (tok == 0)
{ {
plpgsql_error_lineno = yylineno; plpgsql_error_lineno = yylineno;
elog(ERROR, "unexpected end of file"); elog(ERROR, "unexpected end of file");
} }
plpgsql_dstring_append(&ds, yytext); if (tok == K_INTO)
break; {
} if (have_into)
{
plpgsql_error_lineno = yylineno;
elog(ERROR, "INTO specified more than once");
} }
tok = yylex(); tok = yylex();
switch (tok) switch (tok)
{ {
case T_ROW: case T_ROW:
row = yylval.row; row = yylval.row;
have_into = 1;
break; break;
case T_RECORD: case T_RECORD:
rec = yylval.rec; rec = yylval.rec;
have_into = 1;
break; break;
case T_VARIABLE: case T_VARIABLE:
case T_RECFIELD:
{ {
PLpgSQL_var *var;
PLpgSQL_recfield *recfield;
int nfields = 1; int nfields = 1;
char *fieldnames[1024]; char *fieldnames[1024];
int varnos[1024]; int varnos[1024];
switch (tok) check_assignable(yylval.variable);
{
case T_VARIABLE:
var = yylval.var;
fieldnames[0] = strdup(yytext); fieldnames[0] = strdup(yytext);
varnos[0] = var->varno; varnos[0] = yylval.variable->dno;
break;
case T_RECFIELD:
recfield = yylval.recfield;
fieldnames[0] = strdup(yytext);
varnos[0] = recfield->rfno;
break;
}
while ((tok = yylex()) == ',') while ((tok = yylex()) == ',')
{ {
...@@ -1795,22 +1743,19 @@ make_select_stmt() ...@@ -1795,22 +1743,19 @@ make_select_stmt()
switch(tok) switch(tok)
{ {
case T_VARIABLE: case T_VARIABLE:
var = yylval.var; check_assignable(yylval.variable);
fieldnames[nfields] = strdup(yytext); fieldnames[nfields] = strdup(yytext);
varnos[nfields++] = var->varno; varnos[nfields++] = yylval.variable->dno;
break;
case T_RECFIELD:
recfield = yylval.recfield;
fieldnames[0] = strdup(yytext);
varnos[0] = recfield->rfno;
break; break;
default: default:
plpgsql_error_lineno = yylineno; 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 = malloc(sizeof(PLpgSQL_row));
row->dtype = PLPGSQL_DTYPE_ROW; row->dtype = PLPGSQL_DTYPE_ROW;
row->refname = strdup("*internal*"); row->refname = strdup("*internal*");
...@@ -1827,85 +1772,17 @@ make_select_stmt() ...@@ -1827,85 +1772,17 @@ make_select_stmt()
plpgsql_adddatum((PLpgSQL_datum *)row); plpgsql_adddatum((PLpgSQL_datum *)row);
have_nexttok = 1; have_into = 1;
}
break;
default:
{
if (plpgsql_SpaceScanned)
plpgsql_dstring_append(&ds, " ");
plpgsql_dstring_append(&ds, yytext);
while(1)
{
tok = yylex();
if (tok == ';')
{
PLpgSQL_stmt_execsql *execsql;
expr = malloc(sizeof(PLpgSQL_expr) + sizeof(int) * nparams - sizeof(int));
expr->dtype = PLPGSQL_DTYPE_EXPR;
expr->query = strdup(plpgsql_dstring_get(&ds));
expr->plan = NULL;
expr->nparams = nparams;
while (nparams-- > 0)
expr->params[nparams] = params[nparams];
plpgsql_dstring_free(&ds);
execsql = malloc(sizeof(PLpgSQL_stmt_execsql));
execsql->cmd_type = PLPGSQL_STMT_EXECSQL;
execsql->sqlstmt = expr;
return (PLpgSQL_stmt *)execsql;
} }
if (plpgsql_SpaceScanned)
plpgsql_dstring_append(&ds, " ");
switch (tok)
{
case T_VARIABLE:
params[nparams] = yylval.var->varno;
sprintf(buf, " $%d ", ++nparams);
plpgsql_dstring_append(&ds, buf);
break;
case T_RECFIELD:
params[nparams] = yylval.recfield->rfno;
sprintf(buf, " $%d ", ++nparams);
plpgsql_dstring_append(&ds, buf);
break;
case T_TGARGV:
params[nparams] = yylval.trigarg->dno;
sprintf(buf, " $%d ", ++nparams);
plpgsql_dstring_append(&ds, buf);
break; break;
default: default:
if (tok == 0) /* Treat the INTO as non-special */
{ plpgsql_dstring_append(&ds, " INTO ");
plpgsql_error_lineno = yylineno; have_nexttok = 1;
elog(ERROR, "unexpected end of file");
}
plpgsql_dstring_append(&ds, yytext);
break; break;
} }
} continue;
}
}
/************************************************************
* Eat up the rest of the statement after the target fields
************************************************************/
while(1)
{
if (!have_nexttok) {
tok = yylex();
}
have_nexttok = 0;
if (tok == ';') {
break;
} }
if (plpgsql_SpaceScanned) if (plpgsql_SpaceScanned)
...@@ -1913,29 +1790,12 @@ make_select_stmt() ...@@ -1913,29 +1790,12 @@ make_select_stmt()
switch (tok) switch (tok)
{ {
case T_VARIABLE: case T_VARIABLE:
params[nparams] = yylval.var->varno; params[nparams] = yylval.variable->dno;
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); sprintf(buf, " $%d ", ++nparams);
plpgsql_dstring_append(&ds, buf); plpgsql_dstring_append(&ds, buf);
break; break;
default: default:
if (tok == 0)
{
plpgsql_error_lineno = yylineno;
elog(ERROR, "unexpected end of file");
}
plpgsql_dstring_append(&ds, yytext); plpgsql_dstring_append(&ds, yytext);
break; break;
} }
...@@ -1950,6 +1810,10 @@ make_select_stmt() ...@@ -1950,6 +1810,10 @@ make_select_stmt()
expr->params[nparams] = params[nparams]; expr->params[nparams] = params[nparams];
plpgsql_dstring_free(&ds); plpgsql_dstring_free(&ds);
if (have_into)
{
PLpgSQL_stmt_select *select;
select = malloc(sizeof(PLpgSQL_stmt_select)); select = malloc(sizeof(PLpgSQL_stmt_select));
memset(select, 0, sizeof(PLpgSQL_stmt_select)); memset(select, 0, sizeof(PLpgSQL_stmt_select));
select->cmd_type = PLPGSQL_STMT_SELECT; select->cmd_type = PLPGSQL_STMT_SELECT;
...@@ -1958,11 +1822,22 @@ make_select_stmt() ...@@ -1958,11 +1822,22 @@ make_select_stmt()
select->query = expr; 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 * static PLpgSQL_stmt *
make_fetch_stmt() make_fetch_stmt(void)
{ {
int tok; int tok;
PLpgSQL_row *row = NULL; PLpgSQL_row *row = NULL;
...@@ -1970,6 +1845,8 @@ make_fetch_stmt() ...@@ -1970,6 +1845,8 @@ make_fetch_stmt()
PLpgSQL_stmt_fetch *fetch; PLpgSQL_stmt_fetch *fetch;
int have_nexttok = 0; int have_nexttok = 0;
/* We have already parsed everything through the INTO keyword */
tok = yylex(); tok = yylex();
switch (tok) switch (tok)
{ {
...@@ -1982,28 +1859,14 @@ make_fetch_stmt() ...@@ -1982,28 +1859,14 @@ make_fetch_stmt()
break; break;
case T_VARIABLE: case T_VARIABLE:
case T_RECFIELD:
{ {
PLpgSQL_var *var;
PLpgSQL_recfield *recfield;
int nfields = 1; int nfields = 1;
char *fieldnames[1024]; char *fieldnames[1024];
int varnos[1024]; int varnos[1024];
switch (tok) check_assignable(yylval.variable);
{
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); fieldnames[0] = strdup(yytext);
varnos[0] = recfield->rfno; varnos[0] = yylval.variable->dno;
break;
}
while ((tok = yylex()) == ',') while ((tok = yylex()) == ',')
{ {
...@@ -2011,22 +1874,19 @@ make_fetch_stmt() ...@@ -2011,22 +1874,19 @@ make_fetch_stmt()
switch(tok) switch(tok)
{ {
case T_VARIABLE: case T_VARIABLE:
var = yylval.var; check_assignable(yylval.variable);
fieldnames[nfields] = strdup(yytext); fieldnames[nfields] = strdup(yytext);
varnos[nfields++] = var->varno; varnos[nfields++] = yylval.variable->dno;
break;
case T_RECFIELD:
recfield = yylval.recfield;
fieldnames[0] = strdup(yytext);
varnos[0] = recfield->rfno;
break; break;
default: default:
plpgsql_error_lineno = yylineno; 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 = malloc(sizeof(PLpgSQL_row));
row->dtype = PLPGSQL_DTYPE_ROW; row->dtype = PLPGSQL_DTYPE_ROW;
row->refname = strdup("*internal*"); row->refname = strdup("*internal*");
...@@ -2042,8 +1902,6 @@ make_fetch_stmt() ...@@ -2042,8 +1902,6 @@ make_fetch_stmt()
} }
plpgsql_adddatum((PLpgSQL_datum *)row); plpgsql_adddatum((PLpgSQL_datum *)row);
have_nexttok = 1;
} }
break; break;
...@@ -2100,3 +1958,29 @@ make_tupret_expr(PLpgSQL_row *row) ...@@ -2100,3 +1958,29 @@ make_tupret_expr(PLpgSQL_row *row)
plpgsql_dstring_free(&ds); plpgsql_dstring_free(&ds);
return expr; 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;
}
}
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
* procedural language * procedural language
* *
* IDENTIFICATION * 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. * This software is copyrighted by Jan Wieck - Hamburg.
* *
...@@ -269,7 +269,8 @@ plpgsql_compile(Oid fn_oid, int functype) ...@@ -269,7 +269,8 @@ plpgsql_compile(Oid fn_oid, int functype)
row->refname = strdup(buf); row->refname = strdup(buf);
plpgsql_adddatum((PLpgSQL_datum *) row); 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; arg_varnos[i] = row->rowno;
} }
...@@ -299,7 +300,8 @@ plpgsql_compile(Oid fn_oid, int functype) ...@@ -299,7 +300,8 @@ plpgsql_compile(Oid fn_oid, int functype)
var->default_val = NULL; var->default_val = NULL;
plpgsql_adddatum((PLpgSQL_datum *) var); 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; arg_varnos[i] = var->varno;
} }
...@@ -495,7 +497,7 @@ plpgsql_compile(Oid fn_oid, int functype) ...@@ -495,7 +497,7 @@ plpgsql_compile(Oid fn_oid, int functype)
var->default_val = NULL; var->default_val = NULL;
plpgsql_adddatum((PLpgSQL_datum *) var); 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; function->found_varno = var->varno;
/* /*
...@@ -550,19 +552,17 @@ int ...@@ -550,19 +552,17 @@ int
plpgsql_parse_word(char *word) plpgsql_parse_word(char *word)
{ {
PLpgSQL_nsitem *nse; PLpgSQL_nsitem *nse;
char *cp; char *cp[1];
/* /* Do case conversion and word separation */
* We do our lookups case insensitive plpgsql_convert_ident(word, cp, 1);
*/
cp = plpgsql_tolower(word);
/* /*
* Special handling when compiling triggers * Recognize tg_argv when compiling triggers
*/ */
if (plpgsql_curr_compile->fn_functype == T_TRIGGER) 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; int save_spacescanned = plpgsql_SpaceScanned;
PLpgSQL_trigarg *trigarg; PLpgSQL_trigarg *trigarg;
...@@ -577,20 +577,21 @@ plpgsql_parse_word(char *word) ...@@ -577,20 +577,21 @@ plpgsql_parse_word(char *word)
trigarg->argnum = plpgsql_read_expression(']', "]"); trigarg->argnum = plpgsql_read_expression(']', "]");
plpgsql_adddatum((PLpgSQL_datum *) trigarg); plpgsql_adddatum((PLpgSQL_datum *) trigarg);
plpgsql_yylval.trigarg = trigarg; plpgsql_yylval.variable = (PLpgSQL_datum *) trigarg;
plpgsql_SpaceScanned = save_spacescanned; plpgsql_SpaceScanned = save_spacescanned;
return T_TGARGV; pfree(cp[0]);
return T_VARIABLE;
} }
} }
/* /*
* Do a lookup on the compilers namestack * Do a lookup on the compilers namestack
*/ */
nse = plpgsql_ns_lookup(cp, NULL); nse = plpgsql_ns_lookup(cp[0], NULL);
if (nse != NULL) if (nse != NULL)
{ {
pfree(cp); pfree(cp[0]);
switch (nse->itemtype) switch (nse->itemtype)
{ {
case PLPGSQL_NSTYPE_LABEL: case PLPGSQL_NSTYPE_LABEL:
...@@ -617,7 +618,7 @@ plpgsql_parse_word(char *word) ...@@ -617,7 +618,7 @@ plpgsql_parse_word(char *word)
* Nothing found - up to now it's a word without any special meaning * Nothing found - up to now it's a word without any special meaning
* for us. * for us.
*/ */
pfree(cp); pfree(cp[0]);
return T_WORD; return T_WORD;
} }
...@@ -628,26 +629,22 @@ plpgsql_parse_word(char *word) ...@@ -628,26 +629,22 @@ plpgsql_parse_word(char *word)
* ---------- * ----------
*/ */
int int
plpgsql_parse_dblword(char *string) plpgsql_parse_dblword(char *word)
{ {
char *word1;
char *word2;
PLpgSQL_nsitem *ns; PLpgSQL_nsitem *ns;
char *cp[2];
/* /* Do case conversion and word separation */
* Convert to lower case and separate the words plpgsql_convert_ident(word, cp, 2);
*/
word1 = plpgsql_tolower(string);
word2 = strchr(word1, '.');
*word2++ = '\0';
/* /*
* Lookup the first word * Lookup the first word
*/ */
ns = plpgsql_ns_lookup(word1, NULL); ns = plpgsql_ns_lookup(cp[0], NULL);
if (ns == NULL) if (ns == NULL)
{ {
pfree(word1); pfree(cp[0]);
pfree(cp[1]);
return T_ERROR; return T_ERROR;
} }
...@@ -661,33 +658,29 @@ plpgsql_parse_dblword(char *string) ...@@ -661,33 +658,29 @@ plpgsql_parse_dblword(char *string)
* only be something in a query given to the SPI manager and * only be something in a query given to the SPI manager and
* T_ERROR will get eaten up by the collector routines. * 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) if (ns == NULL)
{
pfree(word1);
return T_ERROR; return T_ERROR;
}
switch (ns->itemtype) switch (ns->itemtype)
{ {
case PLPGSQL_NSTYPE_VAR: case PLPGSQL_NSTYPE_VAR:
plpgsql_yylval.var = (PLpgSQL_var *) (plpgsql_Datums[ns->itemno]); plpgsql_yylval.var = (PLpgSQL_var *) (plpgsql_Datums[ns->itemno]);
pfree(word1);
return T_VARIABLE; return T_VARIABLE;
case PLPGSQL_NSTYPE_REC: case PLPGSQL_NSTYPE_REC:
plpgsql_yylval.rec = (PLpgSQL_rec *) (plpgsql_Datums[ns->itemno]); plpgsql_yylval.rec = (PLpgSQL_rec *) (plpgsql_Datums[ns->itemno]);
pfree(word1);
return T_RECORD; return T_RECORD;
case PLPGSQL_NSTYPE_ROW: case PLPGSQL_NSTYPE_ROW:
plpgsql_yylval.row = (PLpgSQL_row *) (plpgsql_Datums[ns->itemno]); plpgsql_yylval.row = (PLpgSQL_row *) (plpgsql_Datums[ns->itemno]);
pfree(word1);
return T_ROW; return T_ROW;
default: default:
pfree(word1);
return T_ERROR; return T_ERROR;
} }
break;
case PLPGSQL_NSTYPE_REC: case PLPGSQL_NSTYPE_REC:
{ {
...@@ -699,14 +692,16 @@ plpgsql_parse_dblword(char *string) ...@@ -699,14 +692,16 @@ plpgsql_parse_dblword(char *string)
new = malloc(sizeof(PLpgSQL_recfield)); new = malloc(sizeof(PLpgSQL_recfield));
new->dtype = PLPGSQL_DTYPE_RECFIELD; new->dtype = PLPGSQL_DTYPE_RECFIELD;
new->fieldname = strdup(word2); new->fieldname = strdup(cp[1]);
new->recno = ns->itemno; new->recno = ns->itemno;
plpgsql_adddatum((PLpgSQL_datum *) new); plpgsql_adddatum((PLpgSQL_datum *) new);
pfree(word1); plpgsql_yylval.variable = (PLpgSQL_datum *) new;
plpgsql_yylval.recfield = new;
return T_RECFIELD; pfree(cp[0]);
pfree(cp[1]);
return T_VARIABLE;
} }
case PLPGSQL_NSTYPE_ROW: case PLPGSQL_NSTYPE_ROW:
...@@ -721,22 +716,24 @@ plpgsql_parse_dblword(char *string) ...@@ -721,22 +716,24 @@ plpgsql_parse_dblword(char *string)
row = (PLpgSQL_row *) (plpgsql_Datums[ns->itemno]); row = (PLpgSQL_row *) (plpgsql_Datums[ns->itemno]);
for (i = 0; i < row->nfields; i++) 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]]); plpgsql_yylval.var = (PLpgSQL_var *) (plpgsql_Datums[row->varnos[i]]);
pfree(word1); pfree(cp[0]);
pfree(cp[1]);
return T_VARIABLE; return T_VARIABLE;
} }
} }
elog(ERROR, "row %s doesn't have a field %s", elog(ERROR, "row %s doesn't have a field %s",
word1, word2); cp[0], cp[1]);
} }
default: default:
break; break;
} }
pfree(word1); pfree(cp[0]);
pfree(cp[1]);
return T_ERROR; return T_ERROR;
} }
...@@ -747,44 +744,42 @@ plpgsql_parse_dblword(char *string) ...@@ -747,44 +744,42 @@ plpgsql_parse_dblword(char *string)
* ---------- * ----------
*/ */
int int
plpgsql_parse_tripword(char *string) plpgsql_parse_tripword(char *word)
{ {
char *word1;
char *word2;
char *word3;
PLpgSQL_nsitem *ns; PLpgSQL_nsitem *ns;
char *cp[3];
/* /* Do case conversion and word separation */
* Convert to lower case and separate the words plpgsql_convert_ident(word, cp, 3);
*/
word1 = plpgsql_tolower(string);
word2 = strchr(word1, '.');
*word2++ = '\0';
word3 = strchr(word2, '.');
*word3++ = '\0';
/* /*
* Lookup the first word - it must be a label * 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) if (ns == NULL)
{ {
pfree(word1); pfree(cp[0]);
pfree(cp[1]);
pfree(cp[2]);
return T_ERROR; return T_ERROR;
} }
if (ns->itemtype != PLPGSQL_NSTYPE_LABEL) if (ns->itemtype != PLPGSQL_NSTYPE_LABEL)
{ {
pfree(word1); pfree(cp[0]);
pfree(cp[1]);
pfree(cp[2]);
return T_ERROR; return T_ERROR;
} }
/* /*
* First word is a label, so second word could be a record or row * 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) if (ns == NULL)
{ {
pfree(word1); pfree(cp[0]);
pfree(cp[1]);
pfree(cp[2]);
return T_ERROR; return T_ERROR;
} }
...@@ -800,14 +795,17 @@ plpgsql_parse_tripword(char *string) ...@@ -800,14 +795,17 @@ plpgsql_parse_tripword(char *string)
new = malloc(sizeof(PLpgSQL_recfield)); new = malloc(sizeof(PLpgSQL_recfield));
new->dtype = PLPGSQL_DTYPE_RECFIELD; new->dtype = PLPGSQL_DTYPE_RECFIELD;
new->fieldname = strdup(word3); new->fieldname = strdup(cp[2]);
new->recno = ns->itemno; new->recno = ns->itemno;
plpgsql_adddatum((PLpgSQL_datum *) new); plpgsql_adddatum((PLpgSQL_datum *) new);
pfree(word1); plpgsql_yylval.variable = (PLpgSQL_datum *) new;
plpgsql_yylval.recfield = new;
return T_RECFIELD; pfree(cp[0]);
pfree(cp[1]);
pfree(cp[2]);
return T_VARIABLE;
} }
case PLPGSQL_NSTYPE_ROW: case PLPGSQL_NSTYPE_ROW:
...@@ -822,22 +820,26 @@ plpgsql_parse_tripword(char *string) ...@@ -822,22 +820,26 @@ plpgsql_parse_tripword(char *string)
row = (PLpgSQL_row *) (plpgsql_Datums[ns->itemno]); row = (PLpgSQL_row *) (plpgsql_Datums[ns->itemno]);
for (i = 0; i < row->nfields; i++) 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]]); plpgsql_yylval.var = (PLpgSQL_var *) (plpgsql_Datums[row->varnos[i]]);
pfree(word1); pfree(cp[0]);
pfree(cp[1]);
pfree(cp[2]);
return T_VARIABLE; return T_VARIABLE;
} }
} }
elog(ERROR, "row %s.%s doesn't have a field %s", elog(ERROR, "row %s.%s doesn't have a field %s",
word1, word2, word3); cp[0], cp[1], cp[2]);
} }
default: default:
break; break;
} }
pfree(word1); pfree(cp[0]);
pfree(cp[1]);
pfree(cp[2]);
return T_ERROR; return T_ERROR;
} }
...@@ -851,27 +853,31 @@ int ...@@ -851,27 +853,31 @@ int
plpgsql_parse_wordtype(char *word) plpgsql_parse_wordtype(char *word)
{ {
PLpgSQL_nsitem *nse; PLpgSQL_nsitem *nse;
char *cp;
bool old_nsstate; bool old_nsstate;
Oid typeOid; Oid typeOid;
char *cp[2];
int i;
/* /* Do case conversion and word separation */
* We do our lookups case insensitive /* We convert %type to .type momentarily to keep converter happy */
*/ i = strlen(word) - 5;
cp = plpgsql_tolower(word); Assert(word[i] == '%');
*(strchr(cp, '%')) = '\0'; 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 * Do a lookup on the compilers namestack. But ensure it moves up to
* the toplevel. * the toplevel.
*/ */
old_nsstate = plpgsql_ns_setlocal(false); old_nsstate = plpgsql_ns_setlocal(false);
nse = plpgsql_ns_lookup(cp, NULL); nse = plpgsql_ns_lookup(cp[0], NULL);
plpgsql_ns_setlocal(old_nsstate); plpgsql_ns_setlocal(old_nsstate);
if (nse != NULL) if (nse != NULL)
{ {
pfree(cp); pfree(cp[0]);
switch (nse->itemtype) switch (nse->itemtype)
{ {
case PLPGSQL_NSTYPE_VAR: case PLPGSQL_NSTYPE_VAR:
...@@ -886,10 +892,8 @@ plpgsql_parse_wordtype(char *word) ...@@ -886,10 +892,8 @@ plpgsql_parse_wordtype(char *word)
/* /*
* Word wasn't found on the namestack. Try to find a data type with * 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. * 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)) if (OidIsValid(typeOid))
{ {
HeapTuple typeTup; HeapTuple typeTup;
...@@ -906,7 +910,7 @@ plpgsql_parse_wordtype(char *word) ...@@ -906,7 +910,7 @@ plpgsql_parse_wordtype(char *word)
typeStruct->typrelid != InvalidOid) typeStruct->typrelid != InvalidOid)
{ {
ReleaseSysCache(typeTup); ReleaseSysCache(typeTup);
pfree(cp); pfree(cp[0]);
return T_ERROR; return T_ERROR;
} }
...@@ -923,7 +927,7 @@ plpgsql_parse_wordtype(char *word) ...@@ -923,7 +927,7 @@ plpgsql_parse_wordtype(char *word)
plpgsql_yylval.dtype = typ; plpgsql_yylval.dtype = typ;
ReleaseSysCache(typeTup); ReleaseSysCache(typeTup);
pfree(cp); pfree(cp[0]);
return T_DTYPE; return T_DTYPE;
} }
} }
...@@ -932,7 +936,7 @@ plpgsql_parse_wordtype(char *word) ...@@ -932,7 +936,7 @@ plpgsql_parse_wordtype(char *word)
* Nothing found - up to now it's a word without any special meaning * Nothing found - up to now it's a word without any special meaning
* for us. * for us.
*/ */
pfree(cp); pfree(cp[0]);
return T_ERROR; return T_ERROR;
} }
...@@ -942,10 +946,8 @@ plpgsql_parse_wordtype(char *word) ...@@ -942,10 +946,8 @@ plpgsql_parse_wordtype(char *word)
* ---------- * ----------
*/ */
int int
plpgsql_parse_dblwordtype(char *string) plpgsql_parse_dblwordtype(char *word)
{ {
char *word1;
char *word2;
PLpgSQL_nsitem *nse; PLpgSQL_nsitem *nse;
bool old_nsstate; bool old_nsstate;
Oid classOid; Oid classOid;
...@@ -956,20 +958,22 @@ plpgsql_parse_dblwordtype(char *string) ...@@ -956,20 +958,22 @@ plpgsql_parse_dblwordtype(char *string)
HeapTuple typetup; HeapTuple typetup;
Form_pg_type typeStruct; Form_pg_type typeStruct;
PLpgSQL_type *typ; PLpgSQL_type *typ;
char *cp[3];
int i;
/* Do case conversion and word separation */
/* /* We convert %type to .type momentarily to keep converter happy */
* Convert to lower case and separate the words i = strlen(word) - 5;
*/ Assert(word[i] == '%');
word1 = plpgsql_tolower(string); word[i] = '.';
word2 = strchr(word1, '.'); plpgsql_convert_ident(word, cp, 3);
*word2++ = '\0'; word[i] = '%';
*(strchr(word2, '%')) = '\0'; pfree(cp[2]);
/* /*
* Lookup the first word * 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 * If this is a label lookup the second word in that labels namestack
...@@ -980,10 +984,11 @@ plpgsql_parse_dblwordtype(char *string) ...@@ -980,10 +984,11 @@ plpgsql_parse_dblwordtype(char *string)
if (nse->itemtype == PLPGSQL_NSTYPE_LABEL) if (nse->itemtype == PLPGSQL_NSTYPE_LABEL)
{ {
old_nsstate = plpgsql_ns_setlocal(false); 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); plpgsql_ns_setlocal(old_nsstate);
pfree(word1); pfree(cp[0]);
pfree(cp[1]);
if (nse != NULL) if (nse != NULL)
{ {
...@@ -999,17 +1004,19 @@ plpgsql_parse_dblwordtype(char *string) ...@@ -999,17 +1004,19 @@ plpgsql_parse_dblwordtype(char *string)
} }
return T_ERROR; return T_ERROR;
} }
pfree(word1); pfree(cp[0]);
pfree(cp[1]);
return T_ERROR; return T_ERROR;
} }
/* /*
* First word could also be a table name * First word could also be a table name
*/ */
classOid = RelnameGetRelid(word1); classOid = RelnameGetRelid(cp[0]);
if (!OidIsValid(classOid)) if (!OidIsValid(classOid))
{ {
pfree(word1); pfree(cp[0]);
pfree(cp[1]);
return T_ERROR; return T_ERROR;
} }
classtup = SearchSysCache(RELOID, classtup = SearchSysCache(RELOID,
...@@ -1017,7 +1024,8 @@ plpgsql_parse_dblwordtype(char *string) ...@@ -1017,7 +1024,8 @@ plpgsql_parse_dblwordtype(char *string)
0, 0, 0); 0, 0, 0);
if (!HeapTupleIsValid(classtup)) if (!HeapTupleIsValid(classtup))
{ {
pfree(word1); pfree(cp[0]);
pfree(cp[1]);
return T_ERROR; return T_ERROR;
} }
...@@ -1030,18 +1038,20 @@ plpgsql_parse_dblwordtype(char *string) ...@@ -1030,18 +1038,20 @@ plpgsql_parse_dblwordtype(char *string)
classStruct->relkind != RELKIND_VIEW) classStruct->relkind != RELKIND_VIEW)
{ {
ReleaseSysCache(classtup); ReleaseSysCache(classtup);
pfree(word1); pfree(cp[0]);
pfree(cp[1]);
return T_ERROR; return T_ERROR;
} }
/* /*
* Fetch the named table field and it's type * Fetch the named table field and it's type
*/ */
attrtup = SearchSysCacheAttName(classOid, word2); attrtup = SearchSysCacheAttName(classOid, cp[1]);
if (!HeapTupleIsValid(attrtup)) if (!HeapTupleIsValid(attrtup))
{ {
ReleaseSysCache(classtup); ReleaseSysCache(classtup);
pfree(word1); pfree(cp[0]);
pfree(cp[1]);
return T_ERROR; return T_ERROR;
} }
attrStruct = (Form_pg_attribute) GETSTRUCT(attrtup); attrStruct = (Form_pg_attribute) GETSTRUCT(attrtup);
...@@ -1051,7 +1061,7 @@ plpgsql_parse_dblwordtype(char *string) ...@@ -1051,7 +1061,7 @@ plpgsql_parse_dblwordtype(char *string)
0, 0, 0); 0, 0, 0);
if (!HeapTupleIsValid(typetup)) if (!HeapTupleIsValid(typetup))
elog(ERROR, "cache lookup for type %u of %s.%s failed", 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); typeStruct = (Form_pg_type) GETSTRUCT(typetup);
/* /*
...@@ -1072,7 +1082,8 @@ plpgsql_parse_dblwordtype(char *string) ...@@ -1072,7 +1082,8 @@ plpgsql_parse_dblwordtype(char *string)
ReleaseSysCache(classtup); ReleaseSysCache(classtup);
ReleaseSysCache(attrtup); ReleaseSysCache(attrtup);
ReleaseSysCache(typetup); ReleaseSysCache(typetup);
pfree(word1); pfree(cp[0]);
pfree(cp[1]);
return T_DTYPE; return T_DTYPE;
} }
...@@ -1083,7 +1094,7 @@ plpgsql_parse_dblwordtype(char *string) ...@@ -1083,7 +1094,7 @@ plpgsql_parse_dblwordtype(char *string)
* ---------- * ----------
*/ */
int int
plpgsql_parse_wordrowtype(char *string) plpgsql_parse_wordrowtype(char *word)
{ {
Oid classOid; Oid classOid;
HeapTuple classtup; HeapTuple classtup;
...@@ -1092,33 +1103,38 @@ plpgsql_parse_wordrowtype(char *string) ...@@ -1092,33 +1103,38 @@ plpgsql_parse_wordrowtype(char *string)
Form_pg_type typeStruct; Form_pg_type typeStruct;
HeapTuple attrtup; HeapTuple attrtup;
Form_pg_attribute attrStruct; Form_pg_attribute attrStruct;
char *word1;
char *cp;
int i;
PLpgSQL_row *row; PLpgSQL_row *row;
PLpgSQL_var *var; 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); classOid = RelnameGetRelid(cp[0]);
cp = strchr(word1, '%');
*cp = '\0';
classOid = RelnameGetRelid(word1);
if (!OidIsValid(classOid)) if (!OidIsValid(classOid))
elog(ERROR, "%s: no such class", word1); elog(ERROR, "%s: no such class", cp[0]);
classtup = SearchSysCache(RELOID, classtup = SearchSysCache(RELOID,
ObjectIdGetDatum(classOid), ObjectIdGetDatum(classOid),
0, 0, 0); 0, 0, 0);
if (!HeapTupleIsValid(classtup)) if (!HeapTupleIsValid(classtup))
elog(ERROR, "%s: no such class", word1); elog(ERROR, "%s: no such class", cp[0]);
classStruct = (Form_pg_class) GETSTRUCT(classtup); classStruct = (Form_pg_class) GETSTRUCT(classtup);
/* accept relation, sequence, or view pg_class entries */ /* accept relation, sequence, or view pg_class entries */
if (classStruct->relkind != RELKIND_RELATION && if (classStruct->relkind != RELKIND_RELATION &&
classStruct->relkind != RELKIND_SEQUENCE && classStruct->relkind != RELKIND_SEQUENCE &&
classStruct->relkind != RELKIND_VIEW) 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 * Create a row datum entry and all the required variables that it
...@@ -1144,17 +1160,17 @@ plpgsql_parse_wordrowtype(char *string) ...@@ -1144,17 +1160,17 @@ plpgsql_parse_wordrowtype(char *string)
0, 0); 0, 0);
if (!HeapTupleIsValid(attrtup)) if (!HeapTupleIsValid(attrtup))
elog(ERROR, "cache lookup for attribute %d of class %s failed", elog(ERROR, "cache lookup for attribute %d of class %s failed",
i + 1, word1); i + 1, cp[0]);
attrStruct = (Form_pg_attribute) GETSTRUCT(attrtup); attrStruct = (Form_pg_attribute) GETSTRUCT(attrtup);
cp = pstrdup(NameStr(attrStruct->attname)); attname = pstrdup(NameStr(attrStruct->attname));
typetup = SearchSysCache(TYPEOID, typetup = SearchSysCache(TYPEOID,
ObjectIdGetDatum(attrStruct->atttypid), ObjectIdGetDatum(attrStruct->atttypid),
0, 0, 0); 0, 0, 0);
if (!HeapTupleIsValid(typetup)) if (!HeapTupleIsValid(typetup))
elog(ERROR, "cache lookup for type %u of %s.%s failed", 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); typeStruct = (Form_pg_type) GETSTRUCT(typetup);
/* /*
...@@ -1170,10 +1186,10 @@ plpgsql_parse_wordrowtype(char *string) ...@@ -1170,10 +1186,10 @@ plpgsql_parse_wordrowtype(char *string)
var = malloc(sizeof(PLpgSQL_var)); var = malloc(sizeof(PLpgSQL_var));
memset(var, 0, sizeof(PLpgSQL_var)); memset(var, 0, sizeof(PLpgSQL_var));
var->dtype = PLPGSQL_DTYPE_VAR; var->dtype = PLPGSQL_DTYPE_VAR;
var->refname = malloc(strlen(word1) + strlen(cp) + 2); var->refname = malloc(strlen(cp[0]) + strlen(attname) + 2);
strcpy(var->refname, word1); strcpy(var->refname, cp[0]);
strcat(var->refname, "."); strcat(var->refname, ".");
strcat(var->refname, cp); strcat(var->refname, attname);
var->datatype = malloc(sizeof(PLpgSQL_type)); var->datatype = malloc(sizeof(PLpgSQL_type));
var->datatype->typname = strdup(NameStr(typeStruct->typname)); var->datatype->typname = strdup(NameStr(typeStruct->typname));
var->datatype->typoid = attrStruct->atttypid; var->datatype->typoid = attrStruct->atttypid;
...@@ -1197,7 +1213,7 @@ plpgsql_parse_wordrowtype(char *string) ...@@ -1197,7 +1213,7 @@ plpgsql_parse_wordrowtype(char *string)
/* /*
* Add the variable to the row. * Add the variable to the row.
*/ */
row->fieldnames[i] = strdup(cp); row->fieldnames[i] = strdup(attname);
row->varnos[i] = var->varno; row->varnos[i] = var->varno;
} }
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
* procedural language * procedural language
* *
* IDENTIFICATION * 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. * This software is copyrighted by Jan Wieck - Hamburg.
* *
...@@ -46,6 +46,10 @@ ...@@ -46,6 +46,10 @@
#include "plpgsql.h" #include "plpgsql.h"
#include "pl.tab.h" #include "pl.tab.h"
#ifdef MULTIBYTE
#include "mb/pg_wchar.h"
#endif
/* ---------- /* ----------
* Local variables for the namestack handling * Local variables for the namestack handling
...@@ -152,6 +156,9 @@ plpgsql_ns_push(char *label) ...@@ -152,6 +156,9 @@ plpgsql_ns_push(char *label)
{ {
PLpgSQL_ns *new; PLpgSQL_ns *new;
if (label == NULL)
label = "";
new = palloc(sizeof(PLpgSQL_ns)); new = palloc(sizeof(PLpgSQL_ns));
memset(new, 0, sizeof(PLpgSQL_ns)); memset(new, 0, sizeof(PLpgSQL_ns));
new->upper = ns_current; new->upper = ns_current;
...@@ -192,9 +199,7 @@ plpgsql_ns_additem(int itemtype, int itemno, char *name) ...@@ -192,9 +199,7 @@ plpgsql_ns_additem(int itemtype, int itemno, char *name)
PLpgSQL_ns *ns = ns_current; PLpgSQL_ns *ns = ns_current;
PLpgSQL_nsitem *nse; PLpgSQL_nsitem *nse;
if (name == NULL) Assert(name != NULL);
name = "";
name = plpgsql_tolower(name);
if (ns->items_used == ns->items_alloc) if (ns->items_used == ns->items_alloc)
{ {
...@@ -322,36 +327,67 @@ plpgsql_ns_rename(char *oldname, char *newname) ...@@ -322,36 +327,67 @@ plpgsql_ns_rename(char *oldname, char *newname)
/* ---------- /* ----------
* plpgsql_tolower Translate a string to lower case * plpgsql_convert_ident
* but honor "" escaping. *
* 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 * void
plpgsql_tolower(char *s) plpgsql_convert_ident(const char *s, char **output, int numidents)
{ {
char *sstart = s; const char *sstart = s;
char *ret; int identctr = 0;
char *cp;
ret = palloc(strlen(s) + 1);
cp = ret;
/* Outer loop over identifiers */
while (*s) while (*s)
{ {
char *curident;
char *cp;
int i;
/* Process current identifier */
curident = palloc(strlen(s) + 1); /* surely enough room */
cp = curident;
if (*s == '"') if (*s == '"')
{ {
/* Quoted identifier: copy, collapsing out doubled quotes */
s++; s++;
while (*s) while (*s)
{ {
if (*s == '"') if (*s == '"')
{
if (s[1] != '"')
break; break;
s++;
}
*cp++ = *s++; *cp++ = *s++;
} }
if (*s != '"') if (*s != '"') /* should not happen if lexer checked */
elog(ERROR, "unterminated \" in name %s", sstart); elog(ERROR, "unterminated \" in name: %s", sstart);
s++; s++;
} }
else else
{
/*
* 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)) if (isupper((unsigned char) *s))
*cp++ = tolower((unsigned char) *s++); *cp++ = tolower((unsigned char) *s++);
...@@ -359,9 +395,47 @@ plpgsql_tolower(char *s) ...@@ -359,9 +395,47 @@ plpgsql_tolower(char *s)
*cp++ = *s++; *cp++ = *s++;
} }
} }
/* Truncate to NAMEDATALEN */
*cp = '\0'; *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);
}
}
return ret; if (identctr != numidents)
elog(ERROR, "Improperly qualified identifier: %s",
sstart);
} }
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
* procedural language * procedural language
* *
* IDENTIFICATION * 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. * This software is copyrighted by Jan Wieck - Hamburg.
* *
...@@ -547,11 +547,11 @@ extern PLpgSQL_function *plpgsql_curr_compile; ...@@ -547,11 +547,11 @@ extern PLpgSQL_function *plpgsql_curr_compile;
*/ */
extern PLpgSQL_function *plpgsql_compile(Oid fn_oid, int functype); extern PLpgSQL_function *plpgsql_compile(Oid fn_oid, int functype);
extern int plpgsql_parse_word(char *word); extern int plpgsql_parse_word(char *word);
extern int plpgsql_parse_dblword(char *string); extern int plpgsql_parse_dblword(char *word);
extern int plpgsql_parse_tripword(char *string); extern int plpgsql_parse_tripword(char *word);
extern int plpgsql_parse_wordtype(char *string); extern int plpgsql_parse_wordtype(char *word);
extern int plpgsql_parse_dblwordtype(char *string); extern int plpgsql_parse_dblwordtype(char *word);
extern int plpgsql_parse_wordrowtype(char *string); extern int plpgsql_parse_wordrowtype(char *word);
extern PLpgSQL_type *plpgsql_parse_datatype(char *string); extern PLpgSQL_type *plpgsql_parse_datatype(char *string);
extern void plpgsql_adddatum(PLpgSQL_datum * new); extern void plpgsql_adddatum(PLpgSQL_datum * new);
extern int plpgsql_add_initdatums(int **varnos); extern int plpgsql_add_initdatums(int **varnos);
...@@ -598,7 +598,7 @@ extern void plpgsql_ns_rename(char *oldname, char *newname); ...@@ -598,7 +598,7 @@ extern void plpgsql_ns_rename(char *oldname, char *newname);
* Other functions in pl_funcs.c * 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 const char *plpgsql_stmt_typename(PLpgSQL_stmt * stmt);
extern void plpgsql_dumptree(PLpgSQL_function * func); extern void plpgsql_dumptree(PLpgSQL_function * func);
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
* procedural language * procedural language
* *
* IDENTIFICATION * 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. * This software is copyrighted by Jan Wieck - Hamburg.
* *
...@@ -60,13 +60,21 @@ static void plpgsql_input(char *buf, int *result, int max); ...@@ -60,13 +60,21 @@ static void plpgsql_input(char *buf, int *result, int max);
%option noyywrap %option noyywrap
%option yylineno %option yylineno
%option case-insensitive
WS [\200-\377_A-Za-z"]
WC [\200-\377_A-Za-z0-9"]
%x IN_STRING IN_COMMENT %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 * Local variable in scanner to remember where
...@@ -154,37 +162,43 @@ dump { return O_DUMP; } ...@@ -154,37 +162,43 @@ dump { return O_DUMP; }
* Special word rules * Special word rules
* ---------- * ----------
*/ */
{WS}{WC}* { return plpgsql_parse_word(yytext); } {identifier} { return plpgsql_parse_word(yytext); }
{WS}{WC}*\.{WS}{WC}* { return plpgsql_parse_dblword(yytext); } {identifier}{space}*\.{space}*{identifier} { return plpgsql_parse_dblword(yytext); }
{WS}{WC}*\.{WS}{WC}*\.{WS}{WC}* { return plpgsql_parse_tripword(yytext); } {identifier}{space}*\.{space}*{identifier}{space}*\.{space}*{identifier} { return plpgsql_parse_tripword(yytext); }
{WS}{WC}*%TYPE { return plpgsql_parse_wordtype(yytext); } {identifier}{space}*%TYPE { return plpgsql_parse_wordtype(yytext); }
{WS}{WC}*\.{WS}{WC}*%TYPE { return plpgsql_parse_dblwordtype(yytext); } {identifier}{space}*\.{space}*{identifier}{space}*%TYPE { return plpgsql_parse_dblwordtype(yytext); }
{WS}{WC}*%ROWTYPE { return plpgsql_parse_wordrowtype(yytext); } {identifier}{space}*%ROWTYPE { return plpgsql_parse_wordrowtype(yytext); }
\$[0-9]+ { return plpgsql_parse_word(yytext); } \${digit}+ { return plpgsql_parse_word(yytext); }
\$[0-9]+\.{WS}{WC}* { return plpgsql_parse_dblword(yytext); } \${digit}+{space}*\.{space}*{identifier} { return plpgsql_parse_dblword(yytext); }
\$[0-9]+\.{WS}{WC}*\.{WS}{WC}* { return plpgsql_parse_tripword(yytext); } \${digit}+{space}*\.{space}*{identifier}{space}*\.{space}*{identifier} { return plpgsql_parse_tripword(yytext); }
\$[0-9]+%TYPE { return plpgsql_parse_wordtype(yytext); } \${digit}+{space}*%TYPE { return plpgsql_parse_wordtype(yytext); }
\$[0-9]+\.{WS}{WC}*%TYPE { return plpgsql_parse_dblwordtype(yytext); } \${digit}+{space}*\.{space}*{identifier}{space}*%TYPE { return plpgsql_parse_dblwordtype(yytext); }
\$[0-9]+%ROWTYPE { return plpgsql_parse_wordrowtype(yytext); } \${digit}+{space}*%ROWTYPE { return plpgsql_parse_wordrowtype(yytext); }
[0-9]+ { return T_NUMBER; } {digit}+ { return T_NUMBER; }
\". {
plpgsql_error_lineno = yylineno;
elog(ERROR, "unterminated quoted identifier");
}
/* ---------- /* ----------
* Ignore whitespaces but remember this happened * Ignore whitespaces but remember this happened
* ---------- * ----------
*/ */
[ \t\r\n]+ { plpgsql_SpaceScanned = 1; } {space}+ { plpgsql_SpaceScanned = 1; }
/* ---------- /* ----------
* Eat up comments * Eat up comments
* ---------- * ----------
*/ */
--[^\r\n]* ; --[^\r\n]* ;
\/\* { start_lineno = yylineno; \/\* { start_lineno = yylineno;
BEGIN IN_COMMENT; BEGIN IN_COMMENT;
} }
<IN_COMMENT>\*\/ { BEGIN INITIAL; } <IN_COMMENT>\*\/ { BEGIN INITIAL; plpgsql_SpaceScanned = 1; }
<IN_COMMENT>\n ; <IN_COMMENT>\n ;
<IN_COMMENT>. ; <IN_COMMENT>. ;
<IN_COMMENT><<EOF>> { <IN_COMMENT><<EOF>> {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment