Skip to content

Commit e91c480

Browse files
feat: Optimise performance
- eliminate one more expensive lookahead - further optimize token manipulation code Signed-off-by: Andreas Reichel <[email protected]>
1 parent b18fbca commit e91c480

File tree

2 files changed

+49
-27
lines changed

2 files changed

+49
-27
lines changed

src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt

Lines changed: 48 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,28 @@ TOKEN_MGR_DECLS : {
184184
}
185185
}
186186

187+
// Finds first occurrence of "\\'"
188+
public static int indexOfSequence(String s, String target) {
189+
int len = s.length();
190+
for (int i = 0; i < len - 1; i++) {
191+
if (s.charAt(i) == '\\' && s.charAt(i + 1) == '\'') {
192+
return i;
193+
}
194+
}
195+
return -1;
196+
}
197+
198+
// Finds last occurrence of "\\''"
199+
public static int lastIndexOfSequence(String s, String target) {
200+
int len = s.length();
201+
for (int i = len - 3; i >= 0; i--) {
202+
if (s.charAt(i) == '\\' && s.charAt(i + 1) == '\'' && s.charAt(i + 2) == '\'') {
203+
return i;
204+
}
205+
}
206+
return -1;
207+
}
208+
187209
public void CommonTokenAction(Token t)
188210
{
189211
t.absoluteBegin = getCurrentTokenAbsolutePosition();
@@ -757,22 +779,26 @@ TOKEN:
757779
// which contains the <SPECIAL_ESC>, then we will need to
758780
// 1) break the <S_CHAR_LITERAL> at <SPECIAL_ESC> close it with a "'"
759781
// 2) continue tokenizing after that <SPECIAL_ESC> with a new <S_CHAR_LITERAL> or any other Token
760-
if ( !configuration.getAsBoolean(Feature.allowBackslashEscapeCharacter)
761-
&& matchedToken.image.contains("\\'") ) {
762-
763-
matchedToken.image = "'" + image.substring( 0, image.indexOf("\\'") + 1 ) + "'";
764-
// `charLiteralIndex` defined in TokenManagerDeclaration above
765-
matchedToken.kind = charLiteralIndex;
766-
input_stream.backup(image.length() + 1 - matchedToken.image.length());
767-
768-
} else if ( configuration.getAsBoolean(Feature.allowBackslashEscapeCharacter)
769-
&& matchedToken.image.contains("\\''") ) {
770-
771-
matchedToken.image = "'" + image.substring( 0, image.lastIndexOf("\\'") + 3);
772-
// `charLiteralIndex` defined in TokenManagerDeclaration above
773-
matchedToken.kind = charLiteralIndex;
774-
input_stream.backup(image.length() + 1 - matchedToken.image.length() );
775-
}
782+
boolean allowEscape = configuration.getAsBoolean(Feature.allowBackslashEscapeCharacter);
783+
String img = matchedToken.image;
784+
int pos;
785+
if (!allowEscape) {
786+
pos = indexOfSequence(img, "\\'");
787+
if (pos > 0) {
788+
matchedToken.image = "'" + image.substring( 0, image.indexOf("\\'") + 1 ) + "'";
789+
// `charLiteralIndex` defined in TokenManagerDeclaration above
790+
matchedToken.kind = charLiteralIndex;
791+
input_stream.backup(image.length() + 1 - matchedToken.image.length());
792+
}
793+
} else {
794+
pos = lastIndexOfSequence(img, "\\''");
795+
if (pos > 0) {
796+
matchedToken.image = "'" + image.substring( 0, image.lastIndexOf("\\'") + 3);
797+
// `charLiteralIndex` defined in TokenManagerDeclaration above
798+
matchedToken.kind = charLiteralIndex;
799+
input_stream.backup(image.length() + 1 - matchedToken.image.length() );
800+
}
801+
}
776802
}
777803
| < S_QUOTED_IDENTIFIER: "\"" ( "\"\"" | ~["\n","\r","\""])* "\"" | "$$" (~["$"])* "$$" | ("`" (~["\n","\r","`"])+ "`") | ( "[" (~["\n","\r","]"])* "]" ) >
778804
{
@@ -2414,16 +2440,12 @@ Select Select() #Select:
24142440
Alias alias = null;
24152441
}
24162442
{
2417-
(
24182443

2419-
//@todo: avoid this expensive semantic look ahead
2420-
LOOKAHEAD( [ WithList() ] FromQuery()) (
2421-
[ with=WithList() ]
2422-
select = FromQuery()
2423-
)
2444+
[ with=WithList() ]
2445+
(
2446+
LOOKAHEAD(3) select = FromQuery()
24242447
|
24252448
(
2426-
[ with=WithList() ]
24272449
(
24282450
LOOKAHEAD(3) select = PlainSelect()
24292451
|
@@ -4998,7 +5020,6 @@ ExpressionList SimpleExpressionList():
49985020
(
49995021
LOOKAHEAD(2, {!interrupted} ) ","
50005022
(
5001-
// @todo: Check hot to avoid this expensive look ahead
50025023
LOOKAHEAD( 6 ) expr=LambdaExpression()
50035024
|
50045025
expr=SimpleExpression()
@@ -5058,7 +5079,6 @@ ExpressionList ComplexExpressionList():
50585079
(
50595080
LOOKAHEAD(2) expr=OracleNamedFunctionParameter()
50605081
|
5061-
// @todo: Check hot to avoid this expensive look ahead
50625082
LOOKAHEAD( 6 ) expr=LambdaExpression()
50635083
| expr=Expression()
50645084
) { expressions.add(expr); }
@@ -5404,7 +5424,9 @@ Expression PrimaryExpression() #PrimaryExpression:
54045424

54055425
| LOOKAHEAD(16) retval=AllTableColumns()
54065426

5407-
// | LOOKAHEAD(250) retval=FunctionAllColumns()
5427+
// See issue #2207
5428+
// there is a huge! performance deterioration from this production
5429+
//| LOOKAHEAD(FunctionAllColumns()) retval=FunctionAllColumns()
54085430

54095431
// support timestamp expressions
54105432
| LOOKAHEAD(2, {!interrupted}) (token=<K_TIME_KEY_EXPR> | token=<K_CURRENT>) { retval = new TimeKeyExpression(token.image); }

src/test/java/net/sf/jsqlparser/benchmark/JSQLParserBenchmark.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ private Path downloadJsqlparserJar(String version) throws IOException {
7474
return jarFile;
7575
}
7676

77-
//@Benchmark
77+
@Benchmark
7878
public void parseSQLStatements(Blackhole blackhole) throws Exception {
7979
final Statements statements = runner.parseStatements(
8080
sqlContent,

0 commit comments

Comments
 (0)