mirror of
https://git.postgresql.org/git/postgresql.git
synced 2024-12-09 08:10:09 +08:00
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.
This commit is contained in:
parent
13e8be42bd
commit
e42f8e32e9
@ -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_ROW
|
||||
%token T_ROWTYPE
|
||||
%token T_RECORD
|
||||
%token T_RECFIELD
|
||||
%token T_TGARGV
|
||||
%token T_DTYPE
|
||||
%token T_WORD
|
||||
%token T_NUMBER
|
||||
%token T_VARIABLE /* a VAR, RECFIELD, or TRIGARG */
|
||||
%token T_ROW
|
||||
%token T_RECORD
|
||||
%token T_DTYPE
|
||||
%token T_LABEL
|
||||
%token T_WORD
|
||||
%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, "%s must be of type cursor or refcursor", yylval.var->refname);
|
||||
elog(ERROR, "cursor variable must be a simple variable");
|
||||
}
|
||||
$$ = yylval.var;
|
||||
if (((PLpgSQL_var *) yylval.variable)->datatype->typoid != REFCURSOROID)
|
||||
{
|
||||
plpgsql_error_lineno = yylineno;
|
||||
elog(ERROR, "%s must be of type cursor or refcursor",
|
||||
((PLpgSQL_var *) yylval.variable)->refname);
|
||||
}
|
||||
$$ = (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, "%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 : ';'
|
||||
;
|
||||
|
||||
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)
|
||||
{
|
||||
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;
|
||||
|
||||
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:
|
||||
{
|
||||
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;
|
||||
}
|
||||
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;
|
||||
|
||||
default:
|
||||
if (tok == 0)
|
||||
{
|
||||
plpgsql_error_lineno = yylineno;
|
||||
elog(ERROR, "unexpected end of file");
|
||||
}
|
||||
plpgsql_dstring_append(&ds, yytext);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/************************************************************
|
||||
* Eat up the rest of the statement after the target fields
|
||||
************************************************************/
|
||||
while(1)
|
||||
{
|
||||
if (!have_nexttok) {
|
||||
if (!have_nexttok)
|
||||
tok = yylex();
|
||||
}
|
||||
have_nexttok = 0;
|
||||
if (tok == ';') {
|
||||
if (tok == ';')
|
||||
break;
|
||||
if (tok == 0)
|
||||
{
|
||||
plpgsql_error_lineno = yylineno;
|
||||
elog(ERROR, "unexpected end of file");
|
||||
}
|
||||
if (tok == K_INTO)
|
||||
{
|
||||
if (have_into)
|
||||
{
|
||||
plpgsql_error_lineno = yylineno;
|
||||
elog(ERROR, "INTO specified more than once");
|
||||
}
|
||||
tok = yylex();
|
||||
switch (tok)
|
||||
{
|
||||
case T_ROW:
|
||||
row = yylval.row;
|
||||
have_into = 1;
|
||||
break;
|
||||
|
||||
case T_RECORD:
|
||||
rec = yylval.rec;
|
||||
have_into = 1;
|
||||
break;
|
||||
|
||||
case T_VARIABLE:
|
||||
{
|
||||
int nfields = 1;
|
||||
char *fieldnames[1024];
|
||||
int varnos[1024];
|
||||
|
||||
check_assignable(yylval.variable);
|
||||
fieldnames[0] = strdup(yytext);
|
||||
varnos[0] = yylval.variable->dno;
|
||||
|
||||
while ((tok = yylex()) == ',')
|
||||
{
|
||||
tok = yylex();
|
||||
switch(tok)
|
||||
{
|
||||
case T_VARIABLE:
|
||||
check_assignable(yylval.variable);
|
||||
fieldnames[nfields] = strdup(yytext);
|
||||
varnos[nfields++] = yylval.variable->dno;
|
||||
break;
|
||||
|
||||
default:
|
||||
plpgsql_error_lineno = yylineno;
|
||||
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*");
|
||||
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_into = 1;
|
||||
}
|
||||
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;
|
||||
|
||||
return (PLpgSQL_stmt *)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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -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];
|
||||
|
||||
/* Do case conversion and word separation */
|
||||
plpgsql_convert_ident(word, cp, 1);
|
||||
|
||||
/*
|
||||
* We do our lookups case insensitive
|
||||
*/
|
||||
cp = plpgsql_tolower(word);
|
||||
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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); }
|
||||
{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); }
|
||||
|
||||
\$[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); }
|
||||
\${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); }
|
||||
|
||||
[0-9]+ { return T_NUMBER; }
|
||||
{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>> {
|
||||
|
Loading…
Reference in New Issue
Block a user