Skip to content

Commit 181a21a

Browse files
Performance Improvements (#1439)
* Adjust Gradle to JUnit 5 Parallel Test execution Gradle Caching Explicitly request for latest JavaCC 7.0.10 * Do not mark SpeedTest for concurrent execution * Remove unused imports * Adjust Gradle to JUnit 5 Parallel Test execution Gradle Caching Explicitly request for latest JavaCC 7.0.10 * Do not mark SpeedTest for concurrent execution * Remove unused imports * Performance Improvements Simplify the Primary Expression Production Try to simple parse without Complex Expressions first, before parsing complex and slow (if supported by max nesting depth) Add Test cases for issues #1397 and #1438 Update Libraries to its latest version Remove JUnit 4 from Gradle * Appease PMD * Update Gradle Plugins to its latest versions Let Parser timeout after 6 seconds and fail gently Add a special test verifying the clean up after timeout * Revert unintended changes to the Test Resources * Appease PMD/Codacy * Correct the Gradle "+" dependencies * Bump version to 4.4.-SNAPSHOT * update build file * revert unwarranted changes in test files * strip the Exception Class Name from the Message * maxDepth = 10 collides with the Parser Timeout = 6 seconds * License Headers Unused imports * Bump version to 4.5-SNAPSHOT Reduce test loops to fit intothe timeout
1 parent 82f63a3 commit 181a21a

File tree

9 files changed

+472
-129
lines changed

9 files changed

+472
-129
lines changed

build.gradle

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
11
plugins {
22
id 'java'
33
id 'maven-publish'
4-
id "ca.coglinc2.javacc" version "3.0.0"
4+
id "ca.coglinc2.javacc" version "latest.release"
55
id 'jacoco'
6-
id "com.github.spotbugs" version "4.7.2"
6+
id "com.github.spotbugs" version "latest.release"
77
id 'pmd'
88
id 'checkstyle'
99

1010
// download the RR tools which have no Maven Repository
11-
id "de.undercouch.download" version "4.1.2"
11+
id "de.undercouch.download" version "latest.release"
1212
}
1313

1414
group = 'com.github.jsqlparser'
15-
version = '4.4-SNAPSHOT'
15+
version = '4.5-SNAPSHOT'
1616
description = 'JSQLParser library'
1717
java.sourceCompatibility = JavaVersion.VERSION_1_8
1818

@@ -23,29 +23,29 @@ repositories {
2323
maven {
2424
url = uri('https://repo.maven.apache.org/maven2/')
2525
}
26+
2627
maven {
2728
url "https://plugins.gradle.org/m2/"
2829
}
2930
}
3031

3132
dependencies {
32-
testImplementation 'commons-io:commons-io:2.11.0'
33-
testImplementation 'junit:junit:4.13.2'
34-
testImplementation 'org.mockito:mockito-core:4.3.1'
35-
testImplementation 'org.assertj:assertj-core:3.22.0'
36-
testImplementation 'org.apache.commons:commons-lang3:3.12.0'
37-
testImplementation 'com.h2database:h2:2.1.210'
33+
testImplementation 'commons-io:commons-io:2.+'
34+
testImplementation 'org.mockito:mockito-core:4.+'
35+
testImplementation 'org.assertj:assertj-core:3.+'
36+
testImplementation 'org.hamcrest:hamcrest-core:2.+'
37+
testImplementation 'org.apache.commons:commons-lang3:3.+'
38+
testImplementation 'com.h2database:h2:2.+'
3839

3940
// for JaCoCo Reports
40-
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.2'
41-
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine'
42-
41+
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.+'
42+
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.+'
43+
4344
// https://mvnrepository.com/artifact/org.mockito/mockito-junit-jupiter
44-
testImplementation 'org.mockito:mockito-junit-jupiter:4.3.1'
45-
45+
testImplementation 'org.mockito:mockito-junit-jupiter:4.+'
46+
4647
// enforce latest version of JavaCC
4748
javacc 'net.java.dev.javacc:javacc:7.0.10'
48-
4949
}
5050

5151
compileJavacc {
@@ -58,7 +58,6 @@ java {
5858

5959
spotbugs
6060
pmd
61-
6261
}
6362

6463
jacoco {
@@ -173,7 +172,7 @@ spotbugs {
173172

174173
pmd {
175174
consoleOutput = false
176-
toolVersion = "6.36.0"
175+
toolVersion = "6.41.0"
177176

178177
sourceSets = [sourceSets.main]
179178

@@ -192,7 +191,7 @@ pmd {
192191
}
193192

194193
checkstyle {
195-
toolVersion "8.45.1"
194+
toolVersion "9.2"
196195
sourceSets = [sourceSets.main, sourceSets.test]
197196
configFile =rootProject.file('config/checkstyle/checkstyle.xml')
198197
}
@@ -254,6 +253,7 @@ task renderRR() {
254253
publishing {
255254
publications {
256255
maven(MavenPublication) {
256+
artifactId 'jsqlparser'
257257
from(components.java)
258258
}
259259
}

ruleset.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ under the License.
103103
<rule ref="category/java/errorprone.xml/UselessOperationOnImmutable" />
104104

105105
<!-- for Codazy -->
106-
<rule ref="category/java/errorprone.xml/MissingBreakInSwitch" />
106+
<!-- <rule ref="category/java/errorprone.xml/MissingBreakInSwitch" /> -->
107107

108108
<rule ref="category/java/multithreading.xml/AvoidThreadGroup" />
109109
<rule ref="category/java/multithreading.xml/DontCallThreadRun" />

src/main/java/net/sf/jsqlparser/parser/CCJSqlParserUtil.java

Lines changed: 140 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,12 @@
1212
import java.io.IOException;
1313
import java.io.InputStream;
1414
import java.io.Reader;
15+
import java.util.concurrent.Callable;
16+
import java.util.concurrent.ExecutorService;
17+
import java.util.concurrent.Executors;
18+
import java.util.concurrent.Future;
19+
import java.util.concurrent.TimeUnit;
20+
import java.util.concurrent.TimeoutException;
1521
import java.util.function.Consumer;
1622
import net.sf.jsqlparser.JSQLParserException;
1723
import net.sf.jsqlparser.expression.Expression;
@@ -23,8 +29,11 @@
2329
*
2430
* @author toben
2531
*/
32+
33+
@SuppressWarnings("PMD.CyclomaticComplexity")
2634
public final class CCJSqlParserUtil {
2735
public final static int ALLOWED_NESTING_DEPTH = 10;
36+
public static final int PARSER_TIMEOUT = 6000;
2837

2938
private CCJSqlParserUtil() {
3039
}
@@ -54,13 +63,25 @@ public static Statement parse(String sql) throws JSQLParserException {
5463
* @throws JSQLParserException
5564
*/
5665
public static Statement parse(String sql, Consumer<CCJSqlParser> consumer) throws JSQLParserException {
57-
boolean allowComplexParsing = getNestingDepth(sql)<=ALLOWED_NESTING_DEPTH;
58-
59-
CCJSqlParser parser = newParser(sql).withAllowComplexParsing(allowComplexParsing);
60-
if (consumer != null) {
61-
consumer.accept(parser);
66+
Statement statement = null;
67+
68+
// first, try to parse fast and simple
69+
try {
70+
CCJSqlParser parser = newParser(sql).withAllowComplexParsing(false);
71+
if (consumer != null) {
72+
consumer.accept(parser);
73+
}
74+
statement = parseStatement(parser);
75+
} catch (JSQLParserException ex) {
76+
if (getNestingDepth(sql)<=ALLOWED_NESTING_DEPTH) {
77+
CCJSqlParser parser = newParser(sql).withAllowComplexParsing(true);
78+
if (consumer != null) {
79+
consumer.accept(parser);
80+
}
81+
statement = parseStatement(parser);
82+
}
6283
}
63-
return parseStatement(parser);
84+
return statement;
6485
}
6586

6687
public static CCJSqlParser newParser(String sql) {
@@ -112,24 +133,44 @@ public static Expression parseExpression(String expression, boolean allowPartial
112133
});
113134
}
114135

115-
public static Expression parseExpression(String expression, boolean allowPartialParse, Consumer<CCJSqlParser> consumer) throws JSQLParserException {
116-
boolean allowComplexParsing = getNestingDepth(expression)<=ALLOWED_NESTING_DEPTH;
117-
118-
CCJSqlParser parser = newParser(expression).withAllowComplexParsing(allowComplexParsing);
119-
if (consumer != null) {
120-
consumer.accept(parser);
121-
}
136+
@SuppressWarnings("PMD.CyclomaticComplexity")
137+
public static Expression parseExpression(String expressionStr, boolean allowPartialParse, Consumer<CCJSqlParser> consumer) throws JSQLParserException {
138+
Expression expression = null;
139+
140+
// first, try to parse fast and simple
122141
try {
123-
Expression expr = parser.Expression();
124-
if (!allowPartialParse && parser.getNextToken().kind != CCJSqlParserTokenManager.EOF) {
125-
throw new JSQLParserException("could only parse partial expression " + expr.toString());
142+
CCJSqlParser parser = newParser(expressionStr).withAllowComplexParsing(false);
143+
if (consumer != null) {
144+
consumer.accept(parser);
145+
}
146+
try {
147+
expression = parser.Expression();
148+
if (parser.getNextToken().kind != CCJSqlParserTokenManager.EOF) {
149+
throw new JSQLParserException("could only parse partial expression " + expression.toString());
150+
}
151+
} catch (ParseException ex) {
152+
throw new JSQLParserException(ex);
153+
}
154+
} catch (JSQLParserException ex1) {
155+
// when fast simple parsing fails, try complex parsing but only if it has a chance to succeed
156+
if (getNestingDepth(expressionStr)<=ALLOWED_NESTING_DEPTH) {
157+
CCJSqlParser parser = newParser(expressionStr).withAllowComplexParsing(true);
158+
if (consumer != null) {
159+
consumer.accept(parser);
160+
}
161+
try {
162+
expression = parser.Expression();
163+
if (!allowPartialParse && parser.getNextToken().kind != CCJSqlParserTokenManager.EOF) {
164+
throw new JSQLParserException("could only parse partial expression " + expression.toString());
165+
}
166+
} catch (JSQLParserException ex) {
167+
throw ex;
168+
} catch (ParseException ex) {
169+
throw new JSQLParserException(ex);
170+
}
126171
}
127-
return expr;
128-
} catch (JSQLParserException ex) {
129-
throw ex;
130-
} catch (ParseException ex) {
131-
throw new JSQLParserException(ex);
132172
}
173+
return expression;
133174
}
134175

135176
/**
@@ -158,24 +199,43 @@ public static Expression parseCondExpression(String condExpr, boolean allowParti
158199
});
159200
}
160201

161-
public static Expression parseCondExpression(String condExpr, boolean allowPartialParse, Consumer<CCJSqlParser> consumer) throws JSQLParserException {
162-
boolean allowComplexParsing = getNestingDepth(condExpr)<=ALLOWED_NESTING_DEPTH;
163-
164-
CCJSqlParser parser = newParser(condExpr).withAllowComplexParsing(allowComplexParsing);
165-
if (consumer != null) {
166-
consumer.accept(parser);
167-
}
202+
@SuppressWarnings("PMD.CyclomaticComplexity")
203+
public static Expression parseCondExpression(String conditionalExpressionStr, boolean allowPartialParse, Consumer<CCJSqlParser> consumer) throws JSQLParserException {
204+
Expression expression = null;
205+
206+
// first, try to parse fast and simple
168207
try {
169-
Expression expr = parser.Expression();
170-
if (!allowPartialParse && parser.getNextToken().kind != CCJSqlParserTokenManager.EOF) {
171-
throw new JSQLParserException("could only parse partial expression " + expr.toString());
208+
CCJSqlParser parser = newParser(conditionalExpressionStr).withAllowComplexParsing(false);
209+
if (consumer != null) {
210+
consumer.accept(parser);
211+
}
212+
try {
213+
expression = parser.Expression();
214+
if (parser.getNextToken().kind != CCJSqlParserTokenManager.EOF) {
215+
throw new JSQLParserException("could only parse partial expression " + expression.toString());
216+
}
217+
} catch (ParseException ex) {
218+
throw new JSQLParserException(ex);
219+
}
220+
} catch (JSQLParserException ex1) {
221+
if (getNestingDepth(conditionalExpressionStr)<=ALLOWED_NESTING_DEPTH) {
222+
CCJSqlParser parser = newParser(conditionalExpressionStr).withAllowComplexParsing(true);
223+
if (consumer != null) {
224+
consumer.accept(parser);
225+
}
226+
try {
227+
expression = parser.Expression();
228+
if (!allowPartialParse && parser.getNextToken().kind != CCJSqlParserTokenManager.EOF) {
229+
throw new JSQLParserException("could only parse partial expression " + expression.toString());
230+
}
231+
} catch (JSQLParserException ex) {
232+
throw ex;
233+
} catch (ParseException ex) {
234+
throw new JSQLParserException(ex);
235+
}
172236
}
173-
return expr;
174-
} catch (JSQLParserException ex) {
175-
throw ex;
176-
} catch (ParseException ex) {
177-
throw new JSQLParserException(ex);
178237
}
238+
return expression;
179239
}
180240

181241
/**
@@ -184,11 +244,25 @@ public static Expression parseCondExpression(String condExpr, boolean allowParti
184244
* @throws JSQLParserException
185245
*/
186246
public static Statement parseStatement(CCJSqlParser parser) throws JSQLParserException {
247+
Statement statement = null;
187248
try {
188-
return parser.Statement();
249+
ExecutorService executorService = Executors.newSingleThreadExecutor();
250+
Future<Statement> future = executorService.submit(new Callable<Statement>() {
251+
@Override
252+
public Statement call() throws Exception {
253+
return parser.Statement();
254+
}
255+
});
256+
executorService.shutdown();
257+
258+
statement = future.get(PARSER_TIMEOUT, TimeUnit.MILLISECONDS);
259+
} catch (TimeoutException ex) {
260+
parser.interrupted = true;
261+
throw new JSQLParserException("Time out occurred.", ex);
189262
} catch (Exception ex) {
190263
throw new JSQLParserException(ex);
191264
}
265+
return statement;
192266
}
193267

194268
/**
@@ -197,10 +271,20 @@ public static Statement parseStatement(CCJSqlParser parser) throws JSQLParserExc
197271
* @return the statements parsed
198272
*/
199273
public static Statements parseStatements(String sqls) throws JSQLParserException {
200-
boolean allowComplexParsing = getNestingDepth(sqls)<=ALLOWED_NESTING_DEPTH;
201-
202-
CCJSqlParser parser = newParser(sqls).withAllowComplexParsing(allowComplexParsing);
203-
return parseStatements(parser);
274+
Statements statements = null;
275+
276+
// first, try to parse fast and simple
277+
try {
278+
CCJSqlParser parser = newParser(sqls).withAllowComplexParsing(false);
279+
statements = parseStatements(parser);
280+
} catch (JSQLParserException ex) {
281+
// when fast simple parsing fails, try complex parsing but only if it has a chance to succeed
282+
if (getNestingDepth(sqls)<=ALLOWED_NESTING_DEPTH) {
283+
CCJSqlParser parser = newParser(sqls).withAllowComplexParsing(true);
284+
statements = parseStatements(parser);
285+
}
286+
}
287+
return statements;
204288
}
205289

206290
/**
@@ -209,11 +293,25 @@ public static Statements parseStatements(String sqls) throws JSQLParserException
209293
* @throws JSQLParserException
210294
*/
211295
public static Statements parseStatements(CCJSqlParser parser) throws JSQLParserException {
296+
Statements statements = null;
212297
try {
213-
return parser.Statements();
298+
ExecutorService executorService = Executors.newSingleThreadExecutor();
299+
Future<Statements> future = executorService.submit(new Callable<Statements>() {
300+
@Override
301+
public Statements call() throws Exception {
302+
return parser.Statements();
303+
}
304+
});
305+
executorService.shutdown();
306+
307+
statements = future.get(PARSER_TIMEOUT, TimeUnit.MILLISECONDS);
308+
} catch (TimeoutException ex) {
309+
parser.interrupted = true;
310+
throw new JSQLParserException("Time out occurred.", ex);
214311
} catch (Exception ex) {
215312
throw new JSQLParserException(ex);
216313
}
314+
return statements;
217315
}
218316

219317
public static void streamStatements(StatementListener listener, InputStream is, String encoding) throws JSQLParserException {

0 commit comments

Comments
 (0)