Received: from malur.postgresql.org ([2a02:16a8:dc51::56]) by arkaria.postgresql.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_CBC_SHA384:256) (Exim 4.89) (envelope-from ) id 1frnWA-0001hg-26 for pgsql-hackers@arkaria.postgresql.org; Mon, 20 Aug 2018 16:59:02 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.89) (envelope-from ) id 1frnW8-0002v8-4s for pgsql-hackers@arkaria.postgresql.org; Mon, 20 Aug 2018 16:59:00 +0000 Received: from magus.postgresql.org ([2a02:c0:301:0:ffff::29]) by malur.postgresql.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_CBC_SHA384:256) (Exim 4.89) (envelope-from ) id 1frnW7-0002v1-Tn for pgsql-hackers@lists.postgresql.org; Mon, 20 Aug 2018 16:58:59 +0000 Received: from lungold.riddles.org.uk ([82.68.208.19]) by magus.postgresql.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_CBC_SHA384:256) (Exim 4.89) (envelope-from ) id 1frnW0-0008RR-Sf for pgsql-hackers@postgresql.org; Mon, 20 Aug 2018 16:58:59 +0000 Received: from [192.168.127.1] (port=14623 helo=caithnard.riddles.org.uk) by lungold.riddles.org.uk with esmtps (TLSv1.2:ECDHE-RSA-AES256-GCM-SHA384:256) (Exim 4.88 (FreeBSD)) (envelope-from ) id 1frnVz-000JK8-91 for pgsql-hackers@postgresql.org; Mon, 20 Aug 2018 16:58:51 +0000 Received: from [127.0.0.1] (port=41558 helo=caithnard.riddles.org.uk) by caithnard.riddles.org.uk with esmtp (Exim 4.89 (FreeBSD)) (envelope-from ) id 1frnVy-000Lgn-GE for pgsql-hackers@postgresql.org; Mon, 20 Aug 2018 16:58:50 +0000 From: Andrew Gierth To: pgsql-hackers@postgresql.org Subject: Re: A really subtle lexer bug In-Reply-To: <87va851ppl.fsf@news-spur.riddles.org.uk> (Andrew Gierth's message of "Mon, 20 Aug 2018 08:38:52 +0100") Message-ID: <87ftz90yma.fsf@news-spur.riddles.org.uk> References: <87va851ppl.fsf@news-spur.riddles.org.uk> User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/25.1 (berkeley-unix) Date: Mon, 20 Aug 2018 17:58:49 +0100 MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Precedence: bulk --=-=-= Content-Type: text/plain >>>>> "Andrew" == Andrew Gierth 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) --=-=-= Content-Type: text/x-patch Content-Disposition: inline; filename=lex.patch Content-Description: lexer patch 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; + } } /* --=-=-=--