Skip to content

Commit 7400013

Browse files
Configurable Parser Timeout via Feature (#1592)
* Configurable Parser Timeout via Feature Fixes #1582 Implement Parser Timeout Feature, e. g. `CCJSqlParserUtil.parse(sqlStr, parser -> parser.withTimeOut(60000));` Add a special test failing after a long time only, to test TimeOut vs. Parser Exception * Appease Codacy * Appease Codacy Co-authored-by: Tobias <t.warneke@gmx.net>
1 parent cfba6e5 commit 7400013

File tree

6 files changed

+83
-4
lines changed

6 files changed

+83
-4
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ Additionally, we have fixed many errors and improved the code quality and the te
7373
* support table option **character set** and **index** options
7474
* support Postgresql optional **TABLE** in **TRUNCATE**
7575
* support for `ANALYZE mytable`
76+
* Implement Parser Timeout Feature, e. g. `CCJSqlParserUtil.parse(sqlStr, parser -> parser.withTimeOut(60000));`
7677
* extended support Postgres' `Extract( field FROM source)` where `field` is a String instead of a Keyword
7778

7879

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,21 @@ public P withAllowComplexParsing(boolean allowComplexParsing) {
3232
public P withUnsupportedStatements(boolean allowUnsupportedStatements) {
3333
return withFeature(Feature.allowUnsupportedStatements, allowUnsupportedStatements);
3434
}
35+
36+
public P withTimeOut(int timeOutMillSeconds) {
37+
return withFeature(Feature.timeOut, timeOutMillSeconds);
38+
}
3539

3640
public P withFeature(Feature f, boolean enabled) {
3741
getConfiguration().setValue(f, enabled);
3842
return me();
3943
}
4044

45+
public P withFeature(Feature f, int value) {
46+
getConfiguration().setValue(f, value);
47+
return me();
48+
}
49+
4150
public abstract FeatureConfiguration getConfiguration();
4251

4352
public abstract P me();
@@ -46,6 +55,10 @@ public boolean getAsBoolean(Feature f) {
4655
return getConfiguration().getAsBoolean(f);
4756
}
4857

58+
public Integer getAsInteger(Feature f) {
59+
return getConfiguration().getAsInteger(f);
60+
}
61+
4962
public void setErrorRecovery(boolean errorRecovery) {
5063
this.errorRecovery = errorRecovery;
5164
}

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import java.util.function.Consumer;
2222
import net.sf.jsqlparser.JSQLParserException;
2323
import net.sf.jsqlparser.expression.Expression;
24+
import net.sf.jsqlparser.parser.feature.Feature;
2425
import net.sf.jsqlparser.statement.Statement;
2526
import net.sf.jsqlparser.statement.Statements;
2627

@@ -33,7 +34,6 @@
3334
@SuppressWarnings("PMD.CyclomaticComplexity")
3435
public final class CCJSqlParserUtil {
3536
public final static int ALLOWED_NESTING_DEPTH = 10;
36-
public static final int PARSER_TIMEOUT = 6000;
3737

3838
private CCJSqlParserUtil() {
3939
}
@@ -255,7 +255,7 @@ public Statement call() throws Exception {
255255
});
256256
executorService.shutdown();
257257

258-
statement = future.get(PARSER_TIMEOUT, TimeUnit.MILLISECONDS);
258+
statement = future.get( parser.getConfiguration().getAsInteger(Feature.timeOut), TimeUnit.MILLISECONDS);
259259
} catch (TimeoutException ex) {
260260
parser.interrupted = true;
261261
throw new JSQLParserException("Time out occurred.", ex);
@@ -319,7 +319,7 @@ public Statements call() throws Exception {
319319
});
320320
executorService.shutdown();
321321

322-
statements = future.get(PARSER_TIMEOUT, TimeUnit.MILLISECONDS);
322+
statements = future.get( parser.getConfiguration().getAsInteger(Feature.timeOut) , TimeUnit.MILLISECONDS);
323323
} catch (TimeoutException ex) {
324324
parser.interrupted = true;
325325
throw new JSQLParserException("Time out occurred.", ex);

src/main/java/net/sf/jsqlparser/parser/feature/Feature.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -741,6 +741,8 @@ public enum Feature {
741741
* needs to be switched off, when VALIDATING statements or parsing blocks
742742
*/
743743
allowUnsupportedStatements(false),
744+
745+
timeOut( 6000)
744746
;
745747

746748
private Object value;

src/main/java/net/sf/jsqlparser/parser/feature/FeatureConfiguration.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,11 @@ public Object getValue(Feature feature) {
5858
}
5959

6060
public boolean getAsBoolean(Feature f) {
61-
return Boolean.valueOf(String.valueOf(getValue(f)));
61+
return Boolean.parseBoolean(String.valueOf(getValue(f)));
62+
}
63+
64+
public Integer getAsInteger(Feature f) {
65+
return Integer.valueOf(String.valueOf(getValue(f)));
6266
}
6367

6468
public String getAsString(Feature f) {

src/test/java/net/sf/jsqlparser/parser/CCJSqlParserUtilTest.java

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
import java.nio.charset.StandardCharsets;
1515
import java.util.ArrayList;
1616
import java.util.List;
17+
import java.util.concurrent.TimeoutException;
18+
1719
import net.sf.jsqlparser.JSQLParserException;
1820
import net.sf.jsqlparser.expression.Expression;
1921
import net.sf.jsqlparser.expression.LongValue;
@@ -29,6 +31,7 @@
2931

3032
import org.junit.jupiter.api.Disabled;
3133
import org.junit.jupiter.api.Test;
34+
import org.junit.jupiter.api.function.Executable;
3235

3336
public class CCJSqlParserUtilTest {
3437

@@ -270,4 +273,60 @@ public void testCondExpressionIssue1482_2() throws JSQLParserException {
270273
Expression expr = CCJSqlParserUtil.parseCondExpression("test_table_enum.f1_enum IN ('TEST2'::test.\"test_enum\")", false);
271274
assertEquals("test_table_enum.f1_enum IN ('TEST2'::test.\"test_enum\")", expr.toString());
272275
}
276+
277+
@Test
278+
public void testTimeOutIssue1582() throws InterruptedException {
279+
// This statement is INVALID on purpose
280+
// There are crafted INTO keywords in order to make it fail but only after a long time (40 seconds plus)
281+
282+
String sqlStr = "" +
283+
"select\n" +
284+
" t0.operatienr\n" +
285+
" , case\n" +
286+
" when\n" +
287+
" case when (t0.vc_begintijd_operatie is null or lpad((extract('hours' into t0.vc_begintijd_operatie::timestamp))::text,2,'0') ||':'|| lpad(extract('minutes' from t0.vc_begintijd_operatie::timestamp)::text,2,'0') = '00:00') then null\n" +
288+
" else (greatest(((extract('hours' into (t0.vc_eindtijd_operatie::timestamp-t0.vc_begintijd_operatie::timestamp))*60 + extract('minutes' from (t0.vc_eindtijd_operatie::timestamp-t0.vc_begintijd_operatie::timestamp)))/60)::numeric(12,2),0))*60\n" +
289+
" end = 0 then null\n" +
290+
" else '25. Meer dan 4 uur'\n" +
291+
" end \n" +
292+
" as snijtijd_interval";
293+
294+
// With DEFAULT TIMEOUT 6 Seconds, we expect the statement to timeout normally
295+
// A TimeoutException wrapped into a Parser Exception should be thrown
296+
297+
assertThrows(TimeoutException.class, new Executable() {
298+
@Override
299+
public void execute() throws Throwable {
300+
try {
301+
CCJSqlParserUtil.parse(sqlStr);
302+
} catch (JSQLParserException ex) {
303+
Throwable cause = ((JSQLParserException) ex).getCause();
304+
if (cause!=null) {
305+
throw cause;
306+
} else {
307+
throw ex;
308+
}
309+
}
310+
}
311+
});
312+
313+
// With custom TIMEOUT 60 Seconds, we expect the statement to not timeout but to fail instead
314+
// No TimeoutException wrapped into a Parser Exception must be thrown
315+
// Instead we expect a Parser Exception only
316+
assertThrows(JSQLParserException.class, new Executable() {
317+
@Override
318+
public void execute() throws Throwable {
319+
try {
320+
CCJSqlParserUtil.parse(sqlStr, parser -> parser.withTimeOut(60000));
321+
} catch (JSQLParserException ex) {
322+
Throwable cause = ((JSQLParserException) ex).getCause();
323+
if (cause instanceof TimeoutException) {
324+
throw cause;
325+
} else {
326+
throw ex;
327+
}
328+
}
329+
}
330+
});
331+
}
273332
}

0 commit comments

Comments
 (0)