Re: A really subtle lexer bug

Поиск
Список
Период
Сортировка
От Andrew Gierth
Тема Re: A really subtle lexer bug
Дата
Msg-id 87ftz90yma.fsf@news-spur.riddles.org.uk
обсуждение исходный текст
Ответ на A really subtle lexer bug  (Andrew Gierth <andrew@tao11.riddles.org.uk>)
Ответы Re: A really subtle lexer bug
Re: A really subtle lexer bug
Список pgsql-hackers
>>>>> "Andrew" == Andrew Gierth <andrew@tao11.riddles.org.uk> writes:

 Andrew> select f(a =>-1);  -- ERROR:  column "a" does not exist

 Andrew> I guess the fix is to extend the existing special case code
 Andrew> that checks for one character left after removing trailing [+-]
 Andrew> and also check for the two-character ops "<>" ">=" "<=" "=>"
 Andrew> "!=".

Patch attached.

This fixes two bugs: first the mis-lexing of two-char ops as mentioned
originally; second, the O(N^3) lexing time of strings of - or +
characters is reduced to O(N^2) (in practice it's better than O(N^2)
once N gets large because the bison stack gets blown out, ending the
loop early).

-- 
Andrew (irc:RhodiumToad)

diff --git a/src/backend/parser/scan.l b/src/backend/parser/scan.l
index 0cd782827a..cc855ffb1c 100644
--- a/src/backend/parser/scan.l
+++ b/src/backend/parser/scan.l
@@ -885,20 +885,34 @@ other            .
                      * to forbid operator names like '?-' that could not be
                      * sequences of SQL operators.
                      */
-                    while (nchars > 1 &&
-                           (yytext[nchars - 1] == '+' ||
-                            yytext[nchars - 1] == '-'))
+                    if (nchars > 1 &&
+                        (yytext[nchars - 1] == '+' ||
+                         yytext[nchars - 1] == '-'))
                     {
                         int            ic;
 
                         for (ic = nchars - 2; ic >= 0; ic--)
                         {
-                            if (strchr("~!@#^&|`?%", yytext[ic]))
+                            char c = yytext[ic];
+                            if (c == '~' || c == '!' || c == '@' ||
+                                c == '#' || c == '^' || c == '&' ||
+                                c == '|' || c == '`' || c == '?' ||
+                                c == '%')
                                 break;
                         }
-                        if (ic >= 0)
-                            break; /* found a char that makes it OK */
-                        nchars--; /* else remove the +/-, and check again */
+                        if (ic < 0)
+                        {
+                            /*
+                             * didn't find a qualifying character, so remove
+                             * all trailing [+-]
+                             */
+                            for (nchars--;
+                                 nchars > 1 &&
+                                     (yytext[nchars - 1] == '+' ||
+                                      yytext[nchars - 1] == '-');
+                                 nchars--)
+                                continue;
+                        }
                     }
 
                     SET_YYLLOC();
@@ -916,6 +930,25 @@ other            .
                         if (nchars == 1 &&
                             strchr(",()[].;:+-*/%^<>=", yytext[0]))
                             return yytext[0];
+                        /*
+                         * Likewise, if what we have left is two chars, and
+                         * those match the tokens ">=", "<=", "=>", "<>" or
+                         * "!=", then we must return the sppropriate token
+                         * rather than the generic Op.
+                         */
+                        if (nchars == 2)
+                        {
+                            if (yytext[0] == '=' && yytext[1] == '>')
+                                return EQUALS_GREATER;
+                            if (yytext[0] == '>' && yytext[1] == '=')
+                                return GREATER_EQUALS;
+                            if (yytext[0] == '<' && yytext[1] == '=')
+                                return LESS_EQUALS;
+                            if (yytext[0] == '<' && yytext[1] == '>')
+                                return NOT_EQUALS;
+                            if (yytext[0] == '!' && yytext[1] == '=')
+                                return NOT_EQUALS;
+                        }
                     }
 
                     /*

В списке pgsql-hackers по дате отправления:

Предыдущее
От: Chris Travers
Дата:
Сообщение: Re: Two proposed modifications to the PostgreSQL FDW
Следующее
От: Chris Travers
Дата:
Сообщение: Re: Two proposed modifications to the PostgreSQL FDW