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
This diff is collapsed.
This diff is collapsed.
...@@ -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,46 +327,115 @@ plpgsql_ns_rename(char *oldname, char *newname) ...@@ -322,46 +327,115 @@ 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 == '"')
break; {
if (s[1] != '"')
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
{ {
if (isupper((unsigned char) *s)) /*
*cp++ = tolower((unsigned char) *s++); * Normal identifier: downcase, stop at dot or whitespace.
else *
*cp++ = *s++; * 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 @@ ...@@ -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