Skip to content

Commit 6c5d7ef

Browse files
fix: improve performance by refactoring Condition, RegularCondition, AndExpression
- fixes #2401 - fixes #1983 - fixes #2140 Signed-off-by: Andreas Reichel <andreas@manticore-projects.com> Signed-off-by: manticore-projects <andreas@manticore-projects.com>
1 parent bd3ce05 commit 6c5d7ef

File tree

17 files changed

+1010
-159
lines changed

17 files changed

+1010
-159
lines changed

build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ dependencies {
154154
testImplementation 'com.h2database:h2:+'
155155

156156
// for JaCoCo Reports
157+
testRuntimeOnly 'org.junit.platform:junit-platform-launcher:1.11.4'
157158
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.11.4'
158159
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.11.4'
159160
testImplementation 'org.junit.jupiter:junit-jupiter-params:5.11.4'

settings.gradle

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
/*
2+
* This file was generated by the Gradle 'init' task.
3+
*/
4+
5+
rootProject.name = 'JSQLParser'

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

Lines changed: 133 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,53 @@ public class CCJSqlParser extends AbstractJSqlParser<CCJSqlParser> {
164164
return true;
165165
}
166166

167+
168+
/**
169+
* Checks whether the given token can start the operator in a RegularCondition
170+
* (comparison operators, JSON operators, regex operators, geometry distance, etc.)
171+
*
172+
* Used to avoid expensive syntactic lookaheads like LOOKAHEAD(RegularCondition()).
173+
* By the time this is called, lower-precedence operators like "-" (subtraction)
174+
* and "||" (concatenation) have already been consumed by SimpleExpression,
175+
* so seeing them here means they are comparison-level operators.
176+
*/
177+
178+
protected boolean isComparisonOperatorAhead() {
179+
try {
180+
Token token = getToken(1);
181+
if (token.image.equals("(") && getToken(2).image.equals("+")) {
182+
// Oracle (+) — only route to RegularConditionRHS if a real
183+
// comparison operator follows after "(" "+" ")"
184+
return isComparisonOperator(getToken(4));
185+
}
186+
return isComparisonOperator(token);
187+
} catch (Exception e) {
188+
return false;
189+
}
190+
}
191+
192+
protected static boolean isComparisonOperator(Token token) {
193+
if (token.image == null || token.image.isEmpty()) {
194+
return false;
195+
}
196+
switch (token.image.charAt(0)) {
197+
case '>': // >, >=
198+
case '=': // =, =*
199+
case '~': // ~, ~*
200+
return true;
201+
case '<': // <, <=, <>, <@, <->, <#>, <=>, <&
202+
return true;
203+
case '*': return token.image.equals("*=");
204+
case '!': return token.image.startsWith("!~") || token.image.startsWith("!=");
205+
case '@': return token.image.equals("@@") || token.image.equals("@>");
206+
case '?': return true; // ?, ?|, ?&
207+
case '-': return true; // -, -#
208+
case '|': return token.image.equals("||");
209+
case '^': return token.image.startsWith("^=");
210+
case '&': return token.image.equals("&&") || token.image.equals("&>");
211+
default: return false;
212+
}
213+
}
167214
}
168215

169216
PARSER_END(CCJSqlParser)
@@ -5994,52 +6041,72 @@ Expression AndExpression() :
59946041
Expression Condition():
59956042
{
59966043
Expression result;
5997-
Token token;
6044+
Expression left;
59986045
boolean not = false;
5999-
boolean exclamationMarkNot = false;
6046+
boolean exclamationMarkNot = false;
6047+
int oraclePrior = EqualsTo.NO_ORACLE_PRIOR;
6048+
int oracleJoin = EqualsTo.NO_ORACLE_JOIN;
60006049
}
60016050
{
60026051
[ LOOKAHEAD(2) (<K_NOT> { not=true; } | "!" { not=true; exclamationMarkNot=true; })]
60036052
(
6004-
LOOKAHEAD(RegularCondition(), {!interrupted}) result=RegularCondition()
6053+
result=ExistsExpression()
60056054
|
6006-
result=SQLCondition()
6007-
)
6055+
[ LOOKAHEAD(2) <K_PRIOR> { oraclePrior = EqualsTo.ORACLE_PRIOR_START; } ]
6056+
left=SimpleExpression() { result = left; }
60086057

6009-
{ return not?new NotExpression(result, exclamationMarkNot):result; }
6010-
}
6011-
6012-
Expression OverlapsCondition():{
6013-
ExpressionList left = new ExpressionList();
6014-
ExpressionList right = new ExpressionList();
6015-
}
6016-
{
6017-
//As per the sql2003 standard, we need at least two items in the list if there is not explicit ROW prefix
6018-
//More than two expression are allowed per the sql2003 grammar.
6019-
left = ParenthesedExpressionList()
6020-
<K_OVERLAPS>
6021-
right = ParenthesedExpressionList()
6058+
// Consume Oracle (+) once, before dispatching
6059+
[
6060+
LOOKAHEAD("(" "+" ")")
6061+
"(" "+" ")"
6062+
{
6063+
oracleJoin = EqualsTo.ORACLE_JOIN_RIGHT;
6064+
if (left instanceof Column) {
6065+
((Column) left).setOldOracleJoinSyntax(oracleJoin);
6066+
}
6067+
}
6068+
]
60226069

6023-
{return new OverlapsCondition(left, right);}
6070+
[
6071+
LOOKAHEAD({ isComparisonOperatorAhead() }) result = RegularConditionRHS(left, oracleJoin)
6072+
|
6073+
LOOKAHEAD(2, <K_OVERLAPS>) result = OverlapsCondition(left)
6074+
|
6075+
LOOKAHEAD(3, {!interrupted}) result=InExpression(left)
6076+
| LOOKAHEAD(3) result=ExcludesExpression(left)
6077+
| LOOKAHEAD(3) result=IncludesExpression(left)
6078+
| LOOKAHEAD(2) result=Between(left)
6079+
| result = MemberOfExpression(left)
6080+
| LOOKAHEAD(3) result=IsNullExpression(left)
6081+
| LOOKAHEAD(3) result=IsBooleanExpression(left)
6082+
| LOOKAHEAD(3) result=IsUnknownExpression(left)
6083+
| LOOKAHEAD(2) result=LikeExpression(left)
6084+
| LOOKAHEAD(3) result=IsDistinctExpression(left)
6085+
| result=SimilarToExpression(left)
6086+
]
6087+
)
6088+
{
6089+
if (oraclePrior == EqualsTo.ORACLE_PRIOR_START
6090+
&& result instanceof SupportsOldOracleJoinSyntax) {
6091+
((SupportsOldOracleJoinSyntax) result).setOraclePriorPosition(oraclePrior);
6092+
}
6093+
return not ? new NotExpression(result, exclamationMarkNot) : result;
6094+
}
60246095
}
60256096

6026-
Expression RegularCondition() #RegularCondition:
6097+
Expression RegularConditionRHS(Expression leftExpression, int oracleJoinRight) #RegularCondition:
60276098
{
60286099
Expression result = null;
6029-
Expression leftExpression;
60306100
Expression rightExpression;
6031-
int oracleJoin=EqualsTo.NO_ORACLE_JOIN;
6032-
int oraclePrior=EqualsTo.NO_ORACLE_PRIOR;
6033-
boolean binary = false;
6034-
boolean not = false;
6101+
int oracleJoin = EqualsTo.NO_ORACLE_JOIN;
6102+
int oraclePrior = EqualsTo.NO_ORACLE_PRIOR;
6103+
Token token;
60356104
}
60366105
{
6037-
[ LOOKAHEAD(2) <K_PRIOR> { oraclePrior = EqualsTo.ORACLE_PRIOR_START; }]
6038-
leftExpression=ComparisonItem() { result = leftExpression; }
6106+
// Only consume (+) here if it wasn't already consumed by Condition
6107+
[ LOOKAHEAD("(" "+" ")") "(" "+" ")" { oracleJoin = EqualsTo.ORACLE_JOIN_RIGHT; } ]
60396108

6040-
[ "(" "+" ")" { oracleJoin=EqualsTo.ORACLE_JOIN_RIGHT; } ]
6041-
6042-
(
6109+
(
60436110
LOOKAHEAD(2)
60446111
">" { result = new GreaterThan(); }
60456112
| "<" { result = new MinorThan(); }
@@ -6059,7 +6126,6 @@ Expression RegularCondition() #RegularCondition:
60596126
| "~*" { result = new RegExpMatchOperator(RegExpMatchOperatorType.MATCH_CASEINSENSITIVE); }
60606127
| "!~" { result = new RegExpMatchOperator(RegExpMatchOperatorType.NOT_MATCH_CASESENSITIVE); }
60616128
| "!~*" { result = new RegExpMatchOperator(RegExpMatchOperatorType.NOT_MATCH_CASEINSENSITIVE); }
6062-
60636129
| "@>" { result = new JsonOperator("@>"); }
60646130
| "<@" { result = new JsonOperator("<@"); }
60656131
| "?" { result = new JsonOperator("?"); }
@@ -6074,28 +6140,46 @@ Expression RegularCondition() #RegularCondition:
60746140
)
60756141

60766142
( LOOKAHEAD(2) <K_PRIOR> rightExpression=ComparisonItem() { oraclePrior = EqualsTo.ORACLE_PRIOR_END; }
6077-
| rightExpression=ComparisonItem() )
6143+
| rightExpression=ComparisonItem() )
60786144

6079-
[ LOOKAHEAD(2) "(" "+" ")" { oracleJoin=EqualsTo.ORACLE_JOIN_LEFT; } ]
6145+
[ LOOKAHEAD(2) "(" "+" ")" { oracleJoin = EqualsTo.ORACLE_JOIN_LEFT; } ]
60806146

60816147
{
60826148
BinaryExpression regCond = (BinaryExpression) result;
60836149
regCond.setLeftExpression(leftExpression);
60846150
regCond.setRightExpression(rightExpression);
60856151

6086-
if (oracleJoin>0)
6087-
((SupportsOldOracleJoinSyntax)result).setOldOracleJoinSyntax(oracleJoin);
6152+
if (oracleJoin > 0)
6153+
((SupportsOldOracleJoinSyntax) result).setOldOracleJoinSyntax(oracleJoin);
60886154

6089-
if (oraclePrior!=EqualsTo.NO_ORACLE_PRIOR)
6090-
((SupportsOldOracleJoinSyntax)result).setOraclePriorPosition(oraclePrior);
6155+
if (oraclePrior != EqualsTo.NO_ORACLE_PRIOR)
6156+
((SupportsOldOracleJoinSyntax) result).setOraclePriorPosition(oraclePrior);
60916157
}
60926158

60936159
{
6094-
linkAST(result,jjtThis);
6160+
linkAST(result, jjtThis);
60956161
return result;
60966162
}
60976163
}
60986164

6165+
Expression OverlapsCondition(Expression leftExpression):
6166+
{
6167+
ExpressionList right;
6168+
}
6169+
{
6170+
<K_OVERLAPS>
6171+
right = ParenthesedExpressionList()
6172+
{
6173+
ExpressionList left;
6174+
if (leftExpression instanceof ExpressionList) {
6175+
left = (ExpressionList) leftExpression;
6176+
} else {
6177+
left = new ExpressionList(leftExpression);
6178+
}
6179+
return new OverlapsCondition(left, right);
6180+
}
6181+
}
6182+
60996183
Expression SQLCondition():
61006184
{
61016185
Expression result;
@@ -6104,10 +6188,11 @@ Expression SQLCondition():
61046188
{
61056189
(
61066190
result=ExistsExpression()
6107-
| LOOKAHEAD( OverlapsCondition(), {!interrupted}) result=OverlapsCondition()
61086191
| left = SimpleExpression() { result = left; }
61096192
[
61106193
LOOKAHEAD(2) (
6194+
LOOKAHEAD(2, <K_OVERLAPS>) result=OverlapsCondition(left)
6195+
|
61116196
LOOKAHEAD(3, {!interrupted}) result=InExpression(left)
61126197
|
61136198
LOOKAHEAD(3) result=ExcludesExpression(left)
@@ -6213,18 +6298,22 @@ Expression Between(Expression leftExpression) :
62136298
(
62146299
LOOKAHEAD( ParenthesedSelect() ) betweenExpressionStart = ParenthesedSelect()
62156300
|
6216-
LOOKAHEAD( RegularCondition() ) betweenExpressionStart = RegularCondition()
6217-
|
6218-
betweenExpressionStart = SimpleExpression()
6301+
betweenExpressionStart = SimpleExpression()
6302+
[
6303+
LOOKAHEAD({ isComparisonOperatorAhead() })
6304+
betweenExpressionStart = RegularConditionRHS(betweenExpressionStart, EqualsTo.NO_ORACLE_JOIN)
6305+
]
62196306
)
62206307

62216308
<K_AND>
62226309
(
62236310
LOOKAHEAD( ParenthesedSelect() ) betweenExpressionEnd = ParenthesedSelect()
62246311
|
6225-
LOOKAHEAD( RegularCondition() ) betweenExpressionEnd = RegularCondition()
6226-
|
62276312
betweenExpressionEnd = SimpleExpression()
6313+
[
6314+
LOOKAHEAD({ isComparisonOperatorAhead() })
6315+
betweenExpressionEnd = RegularConditionRHS(betweenExpressionEnd, EqualsTo.NO_ORACLE_JOIN)
6316+
]
62286317
)
62296318

62306319
{

0 commit comments

Comments
 (0)