Skip to content

Commit 6c5ed68

Browse files
committed
Reduce an unnecessary O(N^3) loop in lexer.
The lexer's handling of operators contained an O(N^3) hazard when dealing with long strings of + or - characters; it seems hard to prevent this case from being O(N^2), but the additional N multiplier was not needed. Backpatch all the way since this has been there since 7.x, and it presents at least a mild hazard in that trying to do Bind, PREPARE or EXPLAIN on a hostile query could take excessive time (without honouring cancels or timeouts) even if the query was never executed.
1 parent 788ae09 commit 6c5ed68

File tree

3 files changed

+63
-24
lines changed

3 files changed

+63
-24
lines changed

src/backend/parser/scan.l

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -844,20 +844,33 @@ other .
844844
* to forbid operator names like '?-' that could not be
845845
* sequences of SQL operators.
846846
*/
847-
while (nchars > 1 &&
848-
(yytext[nchars-1] == '+' ||
849-
yytext[nchars-1] == '-'))
847+
if (nchars > 1 &&
848+
(yytext[nchars - 1] == '+' ||
849+
yytext[nchars - 1] == '-'))
850850
{
851851
int ic;
852852

853-
for (ic = nchars-2; ic >= 0; ic--)
853+
for (ic = nchars - 2; ic >= 0; ic--)
854854
{
855-
if (strchr("~!@#^&|`?%", yytext[ic]))
855+
char c = yytext[ic];
856+
if (c == '~' || c == '!' || c == '@' ||
857+
c == '#' || c == '^' || c == '&' ||
858+
c == '|' || c == '`' || c == '?' ||
859+
c == '%')
856860
break;
857861
}
858-
if (ic >= 0)
859-
break; /* found a char that makes it OK */
860-
nchars--; /* else remove the +/-, and check again */
862+
if (ic < 0)
863+
{
864+
/*
865+
* didn't find a qualifying character, so remove
866+
* all trailing [+-]
867+
*/
868+
do {
869+
nchars--;
870+
} while (nchars > 1 &&
871+
(yytext[nchars - 1] == '+' ||
872+
yytext[nchars - 1] == '-'));
873+
}
861874
}
862875

863876
SET_YYLLOC();

src/bin/psql/psqlscan.l

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -811,20 +811,33 @@ other .
811811
* to forbid operator names like '?-' that could not be
812812
* sequences of SQL operators.
813813
*/
814-
while (nchars > 1 &&
815-
(yytext[nchars-1] == '+' ||
816-
yytext[nchars-1] == '-'))
814+
if (nchars > 1 &&
815+
(yytext[nchars - 1] == '+' ||
816+
yytext[nchars - 1] == '-'))
817817
{
818818
int ic;
819819

820-
for (ic = nchars-2; ic >= 0; ic--)
820+
for (ic = nchars - 2; ic >= 0; ic--)
821821
{
822-
if (strchr("~!@#^&|`?%", yytext[ic]))
822+
char c = yytext[ic];
823+
if (c == '~' || c == '!' || c == '@' ||
824+
c == '#' || c == '^' || c == '&' ||
825+
c == '|' || c == '`' || c == '?' ||
826+
c == '%')
823827
break;
824828
}
825-
if (ic >= 0)
826-
break; /* found a char that makes it OK */
827-
nchars--; /* else remove the +/-, and check again */
829+
if (ic < 0)
830+
{
831+
/*
832+
* didn't find a qualifying character, so remove
833+
* all trailing [+-]
834+
*/
835+
do {
836+
nchars--;
837+
} while (nchars > 1 &&
838+
(yytext[nchars - 1] == '+' ||
839+
yytext[nchars - 1] == '-'));
840+
}
828841
}
829842

830843
if (nchars < yyleng)

src/interfaces/ecpg/preproc/pgc.l

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -675,20 +675,33 @@ cppline {space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})(.*\\{space})*.
675675
* to forbid operator names like '?-' that could not be
676676
* sequences of SQL operators.
677677
*/
678-
while (nchars > 1 &&
679-
(yytext[nchars-1] == '+' ||
680-
yytext[nchars-1] == '-'))
678+
if (nchars > 1 &&
679+
(yytext[nchars - 1] == '+' ||
680+
yytext[nchars - 1] == '-'))
681681
{
682682
int ic;
683683

684-
for (ic = nchars-2; ic >= 0; ic--)
684+
for (ic = nchars - 2; ic >= 0; ic--)
685685
{
686-
if (strchr("~!@#^&|`?%", yytext[ic]))
686+
char c = yytext[ic];
687+
if (c == '~' || c == '!' || c == '@' ||
688+
c == '#' || c == '^' || c == '&' ||
689+
c == '|' || c == '`' || c == '?' ||
690+
c == '%')
687691
break;
688692
}
689-
if (ic >= 0)
690-
break; /* found a char that makes it OK */
691-
nchars--; /* else remove the +/-, and check again */
693+
if (ic < 0)
694+
{
695+
/*
696+
* didn't find a qualifying character, so remove
697+
* all trailing [+-]
698+
*/
699+
do {
700+
nchars--;
701+
} while (nchars > 1 &&
702+
(yytext[nchars - 1] == '+' ||
703+
yytext[nchars - 1] == '-'));
704+
}
692705
}
693706

694707
if (nchars < yyleng)

0 commit comments

Comments
 (0)