-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Performance Improvements #1439
Performance Improvements #1439
Changes from all commits
de805ab
d5a6dca
a9d0503
6dfa05f
8f0bfe6
5cd0974
5f11d3f
b393e00
cb3c91a
cdc56c7
f14cf33
82ce02b
c984ff6
3ab04b0
c075544
79bcbc9
4c38284
e25ee10
de94651
84709a9
cba3125
3b08fde
960aaf2
592de64
31caad9
3c8da30
4cef39b
55f083f
2bd7dee
d7d2dbe
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,6 +12,12 @@ | |
import java.io.IOException; | ||
import java.io.InputStream; | ||
import java.io.Reader; | ||
import java.util.concurrent.Callable; | ||
import java.util.concurrent.ExecutorService; | ||
import java.util.concurrent.Executors; | ||
import java.util.concurrent.Future; | ||
import java.util.concurrent.TimeUnit; | ||
import java.util.concurrent.TimeoutException; | ||
import java.util.function.Consumer; | ||
import net.sf.jsqlparser.JSQLParserException; | ||
import net.sf.jsqlparser.expression.Expression; | ||
|
@@ -23,8 +29,11 @@ | |
* | ||
* @author toben | ||
*/ | ||
|
||
@SuppressWarnings("PMD.CyclomaticComplexity") | ||
public final class CCJSqlParserUtil { | ||
public final static int ALLOWED_NESTING_DEPTH = 10; | ||
public static final int PARSER_TIMEOUT = 6000; | ||
|
||
private CCJSqlParserUtil() { | ||
} | ||
|
@@ -54,13 +63,25 @@ public static Statement parse(String sql) throws JSQLParserException { | |
* @throws JSQLParserException | ||
*/ | ||
public static Statement parse(String sql, Consumer<CCJSqlParser> consumer) throws JSQLParserException { | ||
boolean allowComplexParsing = getNestingDepth(sql)<=ALLOWED_NESTING_DEPTH; | ||
|
||
CCJSqlParser parser = newParser(sql).withAllowComplexParsing(allowComplexParsing); | ||
if (consumer != null) { | ||
consumer.accept(parser); | ||
Statement statement = null; | ||
|
||
// first, try to parse fast and simple | ||
try { | ||
CCJSqlParser parser = newParser(sql).withAllowComplexParsing(false); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If complex parsing is initially switched off, it should not be switched on in this method. Right? However, this is only some kind of hack. The grammar itself is flawed if we need to do something like this. The porblem is the excessive use of Lookups, but if the grammar would be designed that a fixed lookup would be enough, then we do not need a switch like this. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You are correct in your observation, but I respectfully do not share your conclusion:
Best illustration is issue #1444: select doc.fullName from XWikiDocument doc where doc.fullName like :fullName escape '/' and doc.author like :author escape '!' Is this select doc.fullName from XWikiDocument doc where ( doc.fullName like :fullName escape '/' )
and ( doc.author like :author escape '!' ) Or is this select doc.fullName from XWikiDocument doc where doc.fullName like :fullName escape ( '/'
and doc.author like :author escape '!' ) JavaCC by its nature lacks a lot of functionality to deal with those requirements. JavaCC 21 added some features for a reason and I believe ANTLR is there for a reason too. Long story short: So the suggest approach, while not a beauty at all solves that in a pragmatic way: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Of course, if anyone can point me on how to avoid those |
||
if (consumer != null) { | ||
consumer.accept(parser); | ||
} | ||
statement = parseStatement(parser); | ||
} catch (JSQLParserException ex) { | ||
if (getNestingDepth(sql)<=ALLOWED_NESTING_DEPTH) { | ||
CCJSqlParser parser = newParser(sql).withAllowComplexParsing(true); | ||
if (consumer != null) { | ||
consumer.accept(parser); | ||
} | ||
statement = parseStatement(parser); | ||
} | ||
} | ||
return parseStatement(parser); | ||
return statement; | ||
} | ||
|
||
public static CCJSqlParser newParser(String sql) { | ||
|
@@ -112,24 +133,44 @@ public static Expression parseExpression(String expression, boolean allowPartial | |
}); | ||
} | ||
|
||
public static Expression parseExpression(String expression, boolean allowPartialParse, Consumer<CCJSqlParser> consumer) throws JSQLParserException { | ||
boolean allowComplexParsing = getNestingDepth(expression)<=ALLOWED_NESTING_DEPTH; | ||
|
||
CCJSqlParser parser = newParser(expression).withAllowComplexParsing(allowComplexParsing); | ||
if (consumer != null) { | ||
consumer.accept(parser); | ||
} | ||
@SuppressWarnings("PMD.CyclomaticComplexity") | ||
public static Expression parseExpression(String expressionStr, boolean allowPartialParse, Consumer<CCJSqlParser> consumer) throws JSQLParserException { | ||
Expression expression = null; | ||
|
||
// first, try to parse fast and simple | ||
try { | ||
Expression expr = parser.Expression(); | ||
if (!allowPartialParse && parser.getNextToken().kind != CCJSqlParserTokenManager.EOF) { | ||
throw new JSQLParserException("could only parse partial expression " + expr.toString()); | ||
CCJSqlParser parser = newParser(expressionStr).withAllowComplexParsing(false); | ||
if (consumer != null) { | ||
consumer.accept(parser); | ||
} | ||
try { | ||
expression = parser.Expression(); | ||
if (parser.getNextToken().kind != CCJSqlParserTokenManager.EOF) { | ||
throw new JSQLParserException("could only parse partial expression " + expression.toString()); | ||
} | ||
} catch (ParseException ex) { | ||
throw new JSQLParserException(ex); | ||
} | ||
} catch (JSQLParserException ex1) { | ||
// when fast simple parsing fails, try complex parsing but only if it has a chance to succeed | ||
if (getNestingDepth(expressionStr)<=ALLOWED_NESTING_DEPTH) { | ||
CCJSqlParser parser = newParser(expressionStr).withAllowComplexParsing(true); | ||
if (consumer != null) { | ||
consumer.accept(parser); | ||
} | ||
try { | ||
expression = parser.Expression(); | ||
if (!allowPartialParse && parser.getNextToken().kind != CCJSqlParserTokenManager.EOF) { | ||
throw new JSQLParserException("could only parse partial expression " + expression.toString()); | ||
} | ||
} catch (JSQLParserException ex) { | ||
throw ex; | ||
} catch (ParseException ex) { | ||
throw new JSQLParserException(ex); | ||
} | ||
} | ||
return expr; | ||
} catch (JSQLParserException ex) { | ||
throw ex; | ||
} catch (ParseException ex) { | ||
throw new JSQLParserException(ex); | ||
} | ||
return expression; | ||
} | ||
|
||
/** | ||
|
@@ -158,24 +199,43 @@ public static Expression parseCondExpression(String condExpr, boolean allowParti | |
}); | ||
} | ||
|
||
public static Expression parseCondExpression(String condExpr, boolean allowPartialParse, Consumer<CCJSqlParser> consumer) throws JSQLParserException { | ||
boolean allowComplexParsing = getNestingDepth(condExpr)<=ALLOWED_NESTING_DEPTH; | ||
|
||
CCJSqlParser parser = newParser(condExpr).withAllowComplexParsing(allowComplexParsing); | ||
if (consumer != null) { | ||
consumer.accept(parser); | ||
} | ||
@SuppressWarnings("PMD.CyclomaticComplexity") | ||
public static Expression parseCondExpression(String conditionalExpressionStr, boolean allowPartialParse, Consumer<CCJSqlParser> consumer) throws JSQLParserException { | ||
Expression expression = null; | ||
|
||
// first, try to parse fast and simple | ||
try { | ||
Expression expr = parser.Expression(); | ||
if (!allowPartialParse && parser.getNextToken().kind != CCJSqlParserTokenManager.EOF) { | ||
throw new JSQLParserException("could only parse partial expression " + expr.toString()); | ||
CCJSqlParser parser = newParser(conditionalExpressionStr).withAllowComplexParsing(false); | ||
if (consumer != null) { | ||
consumer.accept(parser); | ||
} | ||
try { | ||
expression = parser.Expression(); | ||
if (parser.getNextToken().kind != CCJSqlParserTokenManager.EOF) { | ||
throw new JSQLParserException("could only parse partial expression " + expression.toString()); | ||
} | ||
} catch (ParseException ex) { | ||
throw new JSQLParserException(ex); | ||
} | ||
} catch (JSQLParserException ex1) { | ||
if (getNestingDepth(conditionalExpressionStr)<=ALLOWED_NESTING_DEPTH) { | ||
CCJSqlParser parser = newParser(conditionalExpressionStr).withAllowComplexParsing(true); | ||
if (consumer != null) { | ||
consumer.accept(parser); | ||
} | ||
try { | ||
expression = parser.Expression(); | ||
if (!allowPartialParse && parser.getNextToken().kind != CCJSqlParserTokenManager.EOF) { | ||
throw new JSQLParserException("could only parse partial expression " + expression.toString()); | ||
} | ||
} catch (JSQLParserException ex) { | ||
throw ex; | ||
} catch (ParseException ex) { | ||
throw new JSQLParserException(ex); | ||
} | ||
} | ||
return expr; | ||
} catch (JSQLParserException ex) { | ||
throw ex; | ||
} catch (ParseException ex) { | ||
throw new JSQLParserException(ex); | ||
} | ||
return expression; | ||
} | ||
|
||
/** | ||
|
@@ -184,11 +244,25 @@ public static Expression parseCondExpression(String condExpr, boolean allowParti | |
* @throws JSQLParserException | ||
*/ | ||
public static Statement parseStatement(CCJSqlParser parser) throws JSQLParserException { | ||
Statement statement = null; | ||
try { | ||
return parser.Statement(); | ||
ExecutorService executorService = Executors.newSingleThreadExecutor(); | ||
Future<Statement> future = executorService.submit(new Callable<Statement>() { | ||
@Override | ||
public Statement call() throws Exception { | ||
return parser.Statement(); | ||
} | ||
}); | ||
executorService.shutdown(); | ||
|
||
statement = future.get(PARSER_TIMEOUT, TimeUnit.MILLISECONDS); | ||
} catch (TimeoutException ex) { | ||
parser.interrupted = true; | ||
throw new JSQLParserException("Time out occurred.", ex); | ||
} catch (Exception ex) { | ||
throw new JSQLParserException(ex); | ||
} | ||
return statement; | ||
} | ||
|
||
/** | ||
|
@@ -197,10 +271,20 @@ public static Statement parseStatement(CCJSqlParser parser) throws JSQLParserExc | |
* @return the statements parsed | ||
*/ | ||
public static Statements parseStatements(String sqls) throws JSQLParserException { | ||
boolean allowComplexParsing = getNestingDepth(sqls)<=ALLOWED_NESTING_DEPTH; | ||
|
||
CCJSqlParser parser = newParser(sqls).withAllowComplexParsing(allowComplexParsing); | ||
return parseStatements(parser); | ||
Statements statements = null; | ||
|
||
// first, try to parse fast and simple | ||
try { | ||
CCJSqlParser parser = newParser(sqls).withAllowComplexParsing(false); | ||
statements = parseStatements(parser); | ||
} catch (JSQLParserException ex) { | ||
// when fast simple parsing fails, try complex parsing but only if it has a chance to succeed | ||
if (getNestingDepth(sqls)<=ALLOWED_NESTING_DEPTH) { | ||
CCJSqlParser parser = newParser(sqls).withAllowComplexParsing(true); | ||
statements = parseStatements(parser); | ||
} | ||
} | ||
return statements; | ||
} | ||
|
||
/** | ||
|
@@ -209,11 +293,25 @@ public static Statements parseStatements(String sqls) throws JSQLParserException | |
* @throws JSQLParserException | ||
*/ | ||
public static Statements parseStatements(CCJSqlParser parser) throws JSQLParserException { | ||
Statements statements = null; | ||
try { | ||
return parser.Statements(); | ||
ExecutorService executorService = Executors.newSingleThreadExecutor(); | ||
Future<Statements> future = executorService.submit(new Callable<Statements>() { | ||
@Override | ||
public Statements call() throws Exception { | ||
return parser.Statements(); | ||
} | ||
}); | ||
executorService.shutdown(); | ||
|
||
statements = future.get(PARSER_TIMEOUT, TimeUnit.MILLISECONDS); | ||
} catch (TimeoutException ex) { | ||
parser.interrupted = true; | ||
throw new JSQLParserException("Time out occurred.", ex); | ||
} catch (Exception ex) { | ||
throw new JSQLParserException(ex); | ||
} | ||
return statements; | ||
} | ||
|
||
public static void streamStatements(StatementListener listener, InputStream is, String encoding) throws JSQLParserException { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just curious. Does this work the same way maven dependency versioning works like [1.0,2.0)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I do not know about Maven does (In fact I hate Maven. You can't even
clean
a project while offline. I went straight from ANT to Gradle, which "just understands and does what I want" instead of forcing me into any dogma). Thislatest.release
automatically retrieves the Latest Release of a Gradle Plugin.