Skip to content

Commit 4b983b2

Browse files
perf: trim per-parse allocations that do no work (#2874)
* perf: create the deque of NOT prefixes only for a negated condition `Condition()` created a deque for leading `NOT` and `!` prefixes on every call, though few conditions have one. An empty `ArrayDeque` allocates its 17-slot array up front, so parsing `performance.sql` allocated 585 KB per parse for deques that stayed empty. The deque is now created at the first `NOT` or `!`. * perf: precompile the whitespace pattern of LikeExpression.KeyWord.from `KeyWord.from` runs for every `LIKE`-family condition the parser builds, and `String.replaceAll` compiled its `\s+` pattern on each call. Parsing `performance.sql` allocated 201 KB per parse for those compilations. The pattern is now a precompiled constant. * perf: create PrimaryExpression's sign and JSON operator lists on first use `PrimaryExpression()` created two lists on every call: one for chained signs such as `- -1` and one for JSON operators such as `->`. Nearly every primary expression has neither, so both lists usually stayed empty. They are now created at their first element. * perf: match XML function names without joining the name parts `consumeXmlLeadingKeyword` and `FunctionArgumentList` called `Function.getName()` for every function to check for `XMLPARSE`, `XMLELEMENT` or `XMLFOREST`, and `getName()` joins the name parts into a new string on each call. Both checks now compare the single name part.
1 parent a0e0861 commit 4b983b2

2 files changed

Lines changed: 40 additions & 16 deletions

File tree

‎src/main/java/net/sf/jsqlparser/expression/operators/relational/LikeExpression.java‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
package net.sf.jsqlparser.expression.operators.relational;
1111

1212
import java.util.Locale;
13+
import java.util.regex.Pattern;
1314
import net.sf.jsqlparser.expression.BinaryExpression;
1415
import net.sf.jsqlparser.expression.Expression;
1516
import net.sf.jsqlparser.expression.ExpressionVisitor;
@@ -174,9 +175,11 @@ public LikeExpression withRightExpression(Expression arg0) {
174175
public enum KeyWord {
175176
LIKE, ILIKE, RLIKE, REGEXP_LIKE, REGEXP, GLOB, SIMILAR_TO, MATCH_ANY, MATCH_ALL, MATCH_PHRASE, MATCH_PHRASE_PREFIX, MATCH_REGEXP;
176177

178+
private static final Pattern WHITESPACE = Pattern.compile("\\s+");
179+
177180
public static KeyWord from(String keyword) {
178181
return Enum.valueOf(KeyWord.class,
179-
keyword.toUpperCase(Locale.ROOT).replaceAll("\\s+", "_"));
182+
WHITESPACE.matcher(keyword.toUpperCase(Locale.ROOT)).replaceAll("_"));
180183
}
181184
}
182185

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

Lines changed: 36 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1333,12 +1333,8 @@ public class CCJSqlParser extends AbstractJSqlParser<CCJSqlParser> {
13331333
* everywhere else, e.g. f(content).
13341334
*/
13351335
private void consumeXmlLeadingKeyword(Function function) {
1336-
String name = function.getName();
1337-
if (name == null) {
1338-
return;
1339-
}
1340-
name = name.toUpperCase(java.util.Locale.ROOT);
1341-
if (!"XMLPARSE".equals(name) && !"XMLELEMENT".equals(name) && !"XMLFOREST".equals(name)) {
1336+
if (!hasUnqualifiedName(function, "XMLPARSE") && !hasUnqualifiedName(function, "XMLELEMENT")
1337+
&& !hasUnqualifiedName(function, "XMLFOREST")) {
13421338
return;
13431339
}
13441340
Token token = getToken(1);
@@ -1358,6 +1354,12 @@ public class CCJSqlParser extends AbstractJSqlParser<CCJSqlParser> {
13581354
function.setExtraKeyword(getNextToken().image);
13591355
}
13601356

1357+
// Matches the single name part, since getName() joins the parts into a new string on each call.
1358+
private static boolean hasUnqualifiedName(Function function, String name) {
1359+
List<String> nameParts = function.getMultipartName();
1360+
return nameParts != null && nameParts.size() == 1 && name.equalsIgnoreCase(nameParts.get(0));
1361+
}
1362+
13611363
/**
13621364
* An unquoted YEAR, MONTH, DAY, HOUR, MINUTE or SECOND passed on its own as a function argument, as in
13631365
* DATEADD(day, 1, d), is a date unit. Anywhere else it is an ordinary identifier, so it is parsed as a
@@ -12181,14 +12183,20 @@ Expression Condition():
1218112183
Expression result;
1218212184
Expression left;
1218312185
// Leading NOT / ! prefixes, leftmost token wraps outermost (issue #1170).
12184-
final Deque<Boolean> exclamationMarks = new ArrayDeque<Boolean>();
12186+
Deque<Boolean> exclamationMarks = null;
1218512187
int oraclePrior = EqualsTo.NO_ORACLE_PRIOR;
1218612188
int oracleJoin = EqualsTo.NO_ORACLE_JOIN;
1218712189
}
1218812190
{
1218912191
(
1219012192
LOOKAHEAD({ getToken(1).kind == K_NOT || "!".equals(getToken(1).image) })
12191-
( <K_NOT> { exclamationMarks.addFirst(Boolean.FALSE); } | "!" { exclamationMarks.addFirst(Boolean.TRUE); } )
12193+
( <K_NOT> | "!" )
12194+
{
12195+
if (exclamationMarks == null) {
12196+
exclamationMarks = new ArrayDeque<Boolean>();
12197+
}
12198+
exclamationMarks.addFirst("!".equals(getToken(0).image));
12199+
}
1219212200
)*
1219312201
(
1219412202
LOOKAHEAD(<K_EXISTS> JdbcNamedParameter() ":", {
@@ -12258,7 +12266,7 @@ Expression Condition():
1225812266
&& result instanceof SupportsOldOracleJoinSyntax) {
1225912267
((SupportsOldOracleJoinSyntax) result).setOraclePriorPosition(oraclePrior);
1226012268
}
12261-
while (!exclamationMarks.isEmpty()) {
12269+
while (exclamationMarks != null && !exclamationMarks.isEmpty()) {
1226212270
result = new NotExpression(result, exclamationMarks.pollFirst());
1226312271
}
1226412272
return result;
@@ -13193,21 +13201,29 @@ Expression PrimaryExpression() #PrimaryExpression:
1319313201
Token token = null;
1319413202
Token sign = null;
1319513203
Token innerSign;
13196-
List<Token> innerSigns = new ArrayList<Token>();
13204+
List<Token> innerSigns = null;
1319713205
ColDataType type = null;
1319813206
boolean not = false;
1319913207
boolean exclamationMarkNot = false;
1320013208
boolean dateExpressionAllowed = true;
1320113209
ExpressionList list;
1320213210

13203-
final List<Map.Entry<Expression, String>> jsonIdents = new ArrayList<Map.Entry<Expression, String>>();
13211+
List<Map.Entry<Expression, String>> jsonIdents = null;
1320413212
}
1320513213
{
1320613214
[ <K_NOT> { not=true; } | "!" { not=true; exclamationMarkNot=true; } ]
1320713215
[
1320813216
( sign="+" | sign="-" | sign="~" )
1320913217
// DuckDB's chained signs, e.g. - -1 or +-+1
13210-
( LOOKAHEAD(1) ( innerSign="+" | innerSign="-" | innerSign="~" ) { innerSigns.add(innerSign); } )*
13218+
(
13219+
LOOKAHEAD(1) ( innerSign="+" | innerSign="-" | innerSign="~" )
13220+
{
13221+
if (innerSigns == null) {
13222+
innerSigns = new ArrayList<Token>();
13223+
}
13224+
innerSigns.add(innerSign);
13225+
}
13226+
)*
1321113227
]
1321213228
(
1321313229
<K_NULL> { retval = new NullValue(); }
@@ -13406,6 +13422,9 @@ Expression PrimaryExpression() #PrimaryExpression:
1340613422
expression=SimpleExpression()
1340713423
)
1340813424
{
13425+
if (jsonIdents == null) {
13426+
jsonIdents = new ArrayList<Map.Entry<Expression, String>>();
13427+
}
1340913428
jsonIdents.add(new AbstractMap.SimpleEntry<Expression, String>(expression, token.image ));
1341013429
}
1341113430
)+
@@ -13425,8 +13444,10 @@ Expression PrimaryExpression() #PrimaryExpression:
1342513444
timezoneExpr.setLeftExpression(retval);
1342613445
retval=timezoneExpr;
1342713446
}
13428-
for (int i = innerSigns.size() - 1; i >= 0; i--) {
13429-
retval = new SignedExpression(innerSigns.get(i).image.charAt(0), retval);
13447+
if (innerSigns != null) {
13448+
for (int i = innerSigns.size() - 1; i >= 0; i--) {
13449+
retval = new SignedExpression(innerSigns.get(i).image.charAt(0), retval);
13450+
}
1343013451
}
1343113452
if (sign != null) {
1343213453
retval = new SignedExpression(sign.image.charAt(0), retval);
@@ -15421,7 +15442,7 @@ ExpressionList FunctionArgumentList(Function retval):
1542115442
Token extraKeywordToken;
1542215443
}
1542315444
{
15424-
{ if ("XMLFOREST".equalsIgnoreCase(retval.getName())) { return XmlForestArguments(); } }
15445+
{ if (hasUnqualifiedName(retval, "XMLFOREST")) { return XmlForestArguments(); } }
1542515446
[ LOOKAHEAD(2) extraKeywordToken = <K_TABLE> { retval.setExtraKeyword(extraKeywordToken.image); } ]
1542615447
expressionList=ExpressionList()
1542715448
// explicit lookahead: InternalFunction accepts ORDER BY after IGNORE NULLS too

0 commit comments

Comments
 (0)