Skip to content

Commit d4a63f8

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 5ca0077 commit d4a63f8

File tree

3 files changed

+61
-22
lines changed

3 files changed

+61
-22
lines changed

src/backend/parser/scan.l

+20-7
Original file line numberDiff line numberDiff line change
@@ -885,20 +885,33 @@ other .
885885
* to forbid operator names like '?-' that could not be
886886
* sequences of SQL operators.
887887
*/
888-
while (nchars > 1 &&
889-
(yytext[nchars - 1] == '+' ||
890-
yytext[nchars - 1] == '-'))
888+
if (nchars > 1 &&
889+
(yytext[nchars - 1] == '+' ||
890+
yytext[nchars - 1] == '-'))
891891
{
892892
int ic;
893893

894894
for (ic = nchars - 2; ic >= 0; ic--)
895895
{
896-
if (strchr("~!@#^&|`?%", yytext[ic]))
896+
char c = yytext[ic];
897+
if (c == '~' || c == '!' || c == '@' ||
898+
c == '#' || c == '^' || c == '&' ||
899+
c == '|' || c == '`' || c == '?' ||
900+
c == '%')
897901
break;
898902
}
899-
if (ic >= 0)
900-
break; /* found a char that makes it OK */
901-
nchars--; /* else remove the +/-, and check again */
903+
if (ic < 0)
904+
{
905+
/*
906+
* didn't find a qualifying character, so remove
907+
* all trailing [+-]
908+
*/
909+
do {
910+
nchars--;
911+
} while (nchars > 1 &&
912+
(yytext[nchars - 1] == '+' ||
913+
yytext[nchars - 1] == '-'));
914+
}
902915
}
903916

904917
SET_YYLLOC();

src/fe_utils/psqlscan.l

+20-7
Original file line numberDiff line numberDiff line change
@@ -817,20 +817,33 @@ other .
817817
* to forbid operator names like '?-' that could not be
818818
* sequences of SQL operators.
819819
*/
820-
while (nchars > 1 &&
821-
(yytext[nchars - 1] == '+' ||
822-
yytext[nchars - 1] == '-'))
820+
if (nchars > 1 &&
821+
(yytext[nchars - 1] == '+' ||
822+
yytext[nchars - 1] == '-'))
823823
{
824824
int ic;
825825

826826
for (ic = nchars - 2; ic >= 0; ic--)
827827
{
828-
if (strchr("~!@#^&|`?%", yytext[ic]))
828+
char c = yytext[ic];
829+
if (c == '~' || c == '!' || c == '@' ||
830+
c == '#' || c == '^' || c == '&' ||
831+
c == '|' || c == '`' || c == '?' ||
832+
c == '%')
829833
break;
830834
}
831-
if (ic >= 0)
832-
break; /* found a char that makes it OK */
833-
nchars--; /* else remove the +/-, and check again */
835+
if (ic < 0)
836+
{
837+
/*
838+
* didn't find a qualifying character, so remove
839+
* all trailing [+-]
840+
*/
841+
do {
842+
nchars--;
843+
} while (nchars > 1 &&
844+
(yytext[nchars - 1] == '+' ||
845+
yytext[nchars - 1] == '-'));
846+
}
834847
}
835848

836849
if (nchars < yyleng)

src/interfaces/ecpg/preproc/pgc.l

+21-8
Original file line numberDiff line numberDiff line change
@@ -690,20 +690,33 @@ cppline {space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+
690690
* to forbid operator names like '?-' that could not be
691691
* sequences of SQL operators.
692692
*/
693-
while (nchars > 1 &&
694-
(yytext[nchars-1] == '+' ||
695-
yytext[nchars-1] == '-'))
693+
if (nchars > 1 &&
694+
(yytext[nchars - 1] == '+' ||
695+
yytext[nchars - 1] == '-'))
696696
{
697697
int ic;
698698

699-
for (ic = nchars-2; ic >= 0; ic--)
699+
for (ic = nchars - 2; ic >= 0; ic--)
700700
{
701-
if (strchr("~!@#^&|`?%", yytext[ic]))
701+
char c = yytext[ic];
702+
if (c == '~' || c == '!' || c == '@' ||
703+
c == '#' || c == '^' || c == '&' ||
704+
c == '|' || c == '`' || c == '?' ||
705+
c == '%')
702706
break;
703707
}
704-
if (ic >= 0)
705-
break; /* found a char that makes it OK */
706-
nchars--; /* else remove the +/-, and check again */
708+
if (ic < 0)
709+
{
710+
/*
711+
* didn't find a qualifying character, so remove
712+
* all trailing [+-]
713+
*/
714+
do {
715+
nchars--;
716+
} while (nchars > 1 &&
717+
(yytext[nchars - 1] == '+' ||
718+
yytext[nchars - 1] == '-'));
719+
}
707720
}
708721

709722
if (nchars < yyleng)

0 commit comments

Comments
 (0)