Skip to content

Commit 1e6849c

Browse files
committed
perf: cache lexical configuration lookups and trim per-expression checks
The token actions query the wired FeatureConfiguration for every identifier, string literal and quoted identifier; PrimaryExpression pays new predicate checks after every expression. Cache the derived dialect/flag answers on the wired configuration's identity (it is only mutated before parsing starts), lazily allocate the DuckDB chained-sign list, reject non-operator images before the PostgreSQL json-access equals chain, and hoist the unsupported statements flag out of the capture loop. Signed-off-by: 付典 <fudianchn@gmail.com>
1 parent b3fc3b4 commit 1e6849c

1 file changed

Lines changed: 117 additions & 20 deletions

File tree

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

Lines changed: 117 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,10 @@ public class CCJSqlParser extends AbstractJSqlParser<CCJSqlParser> {
182182
}
183183

184184
private void captureOpaqueStatements(Statements statements) throws ParseException {
185-
while (getAsBoolean(Feature.allowUnsupportedStatements) && isOpaqueStatementAhead()) {
185+
if (!getAsBoolean(Feature.allowUnsupportedStatements)) {
186+
return;
187+
}
188+
while (isOpaqueStatementAhead()) {
186189
statements.add(UnsupportedStatement());
187190
while (getToken(1).kind == ST_SEMICOLON) {
188191
getNextToken();
@@ -694,8 +697,14 @@ public class CCJSqlParser extends AbstractJSqlParser<CCJSqlParser> {
694697
}
695698

696699
private boolean isPostgreSqlJsonAccess(Token token) {
697-
return ("->".equals(token.image) || "->>".equals(token.image)
698-
|| "#>".equals(token.image) || "#>>".equals(token.image))
700+
// reject non-operator images with one charAt before the equals chain: this
701+
// runs after every primary expression
702+
String image = token.image;
703+
if (image == null || image.length() < 2 || (image.charAt(0) != '-' && image.charAt(0) != '#')) {
704+
return false;
705+
}
706+
return ("->".equals(image) || "->>".equals(image)
707+
|| "#>".equals(image) || "#>>".equals(image))
699708
&& Dialect.POSTGRESQL.name().equals(getAsString(Feature.dialect));
700709
}
701710

@@ -2704,6 +2713,95 @@ PARSER_END(CCJSqlParser)
27042713
TOKEN_MGR_DECLS : {
27052714
public FeatureConfiguration configuration = new FeatureConfiguration();
27062715

2716+
// The configuration is wired before parsing starts (CCJSqlParser.getConfiguration /
2717+
// withConfiguration) and no lexical action mutates it mid-parse. Cache the dialect and
2718+
// flag lookups the per-token actions need, keyed on the wired configuration's identity,
2719+
// so every token pays a field read instead of a map lookup.
2720+
private FeatureConfiguration lexicalConfiguration;
2721+
private boolean lexicalPostgreSqlDialect;
2722+
private boolean lexicalBigQueryDialect;
2723+
private boolean lexicalAllowHashLineComments;
2724+
private boolean lexicalAllowBackslashEscapes;
2725+
private boolean lexicalAllowDoubleQuotedStrings;
2726+
private boolean lexicalAllowSquareBracketQuotation;
2727+
2728+
private void refreshLexicalConfiguration(FeatureConfiguration wired) {
2729+
lexicalConfiguration = wired;
2730+
Object dialect = wired.getValue(Feature.dialect);
2731+
lexicalPostgreSqlDialect = AbstractJSqlParser.Dialect.POSTGRESQL.name().equals(dialect);
2732+
lexicalBigQueryDialect = AbstractJSqlParser.Dialect.BIGQUERY.name().equals(dialect);
2733+
lexicalAllowHashLineComments = Boolean.TRUE.equals(wired.getValue(Feature.allowHashLineComments));
2734+
lexicalAllowBackslashEscapes = wired.getAsBoolean(Feature.allowBackslashEscapeCharacter);
2735+
lexicalAllowDoubleQuotedStrings = wired.getAsBoolean(Feature.allowDoubleQuotedStrings);
2736+
lexicalAllowSquareBracketQuotation = wired.getAsBoolean(Feature.allowSquareBracketQuotation);
2737+
}
2738+
2739+
private boolean isPostgreSqlDialect() {
2740+
FeatureConfiguration wired = input_stream.featureConfiguration;
2741+
if (wired == null) {
2742+
return false;
2743+
}
2744+
if (wired != lexicalConfiguration) {
2745+
refreshLexicalConfiguration(wired);
2746+
}
2747+
return lexicalPostgreSqlDialect;
2748+
}
2749+
2750+
private boolean isBigQueryDialect() {
2751+
FeatureConfiguration wired = input_stream.featureConfiguration;
2752+
if (wired == null) {
2753+
return false;
2754+
}
2755+
if (wired != lexicalConfiguration) {
2756+
refreshLexicalConfiguration(wired);
2757+
}
2758+
return lexicalBigQueryDialect;
2759+
}
2760+
2761+
private boolean isAllowHashLineComments() {
2762+
FeatureConfiguration wired = input_stream.featureConfiguration;
2763+
if (wired == null) {
2764+
return false;
2765+
}
2766+
if (wired != lexicalConfiguration) {
2767+
refreshLexicalConfiguration(wired);
2768+
}
2769+
return lexicalAllowHashLineComments;
2770+
}
2771+
2772+
private boolean isAllowBackslashEscapes() {
2773+
FeatureConfiguration wired = input_stream.featureConfiguration;
2774+
if (wired == null) {
2775+
return false;
2776+
}
2777+
if (wired != lexicalConfiguration) {
2778+
refreshLexicalConfiguration(wired);
2779+
}
2780+
return lexicalAllowBackslashEscapes;
2781+
}
2782+
2783+
private boolean isAllowDoubleQuotedStrings() {
2784+
FeatureConfiguration wired = input_stream.featureConfiguration;
2785+
if (wired == null) {
2786+
return false;
2787+
}
2788+
if (wired != lexicalConfiguration) {
2789+
refreshLexicalConfiguration(wired);
2790+
}
2791+
return lexicalAllowDoubleQuotedStrings;
2792+
}
2793+
2794+
private boolean isAllowSquareBracketQuotation() {
2795+
FeatureConfiguration wired = input_stream.featureConfiguration;
2796+
if (wired == null) {
2797+
return false;
2798+
}
2799+
if (wired != lexicalConfiguration) {
2800+
refreshLexicalConfiguration(wired);
2801+
}
2802+
return lexicalAllowSquareBracketQuotation;
2803+
}
2804+
27072805

27082806
// Identify the index of the quoting/escaping tokens
27092807
public int charLiteralIndex = -1;
@@ -2768,7 +2866,7 @@ TOKEN_MGR_DECLS : {
27682866
// disambiguation can fetch PATTERN tokens before its production is entered.
27692867
if (rowPatternContext == null && t.kind == S_IDENTIFIER
27702868
&& "MATCH_RECOGNIZE".equalsIgnoreCase(t.image)
2771-
&& AbstractJSqlParser.Dialect.BIGQUERY.name().equals(configuration.getValue(Feature.dialect))) {
2869+
&& isBigQueryDialect()) {
27722870
rowPatternContext = new RowPatternTokenContext();
27732871
}
27742872
if (rowPatternContext != null) {
@@ -3640,17 +3738,15 @@ TOKEN:
36403738
// that never opted in (the stream is only wired through the feature
36413739
// consumers / withConfiguration); getValue avoids the String-based
36423740
// getAsBoolean roundtrip
3643-
if (matchedToken.kind == S_IDENTIFIER && input_stream.featureConfiguration != null
3644-
&& Boolean.TRUE.equals(configuration.getValue(Feature.allowHashLineComments))) {
3741+
if (matchedToken.kind == S_IDENTIFIER && isAllowHashLineComments()) {
36453742
int hashIndex = matchedToken.image.indexOf('#');
36463743
if (hashIndex > 0) {
36473744
truncateIdentifierToken(matchedToken, hashIndex);
36483745
}
36493746
}
36503747
// PostgreSQL does not allow # or @ in unquoted identifiers. Re-lex them as
36513748
// operators, including #>, #>>, #- and @?, when touching the left operand.
3652-
if (matchedToken.kind == S_IDENTIFIER
3653-
&& AbstractJSqlParser.Dialect.POSTGRESQL.name().equals(configuration.getValue(Feature.dialect))) {
3749+
if (matchedToken.kind == S_IDENTIFIER && isPostgreSqlDialect()) {
36543750
int hashIndex = matchedToken.image.indexOf('#');
36553751
int atIndex = matchedToken.image.indexOf('@');
36563752
int operatorIndex = hashIndex < 0 ? atIndex
@@ -3676,8 +3772,7 @@ TOKEN:
36763772
// that never opted in (the stream is only wired through the feature
36773773
// consumers / withConfiguration); getValue avoids the String-based
36783774
// getAsBoolean roundtrip
3679-
if (input_stream.featureConfiguration != null
3680-
&& Boolean.TRUE.equals(configuration.getValue(Feature.allowHashLineComments))) {
3775+
if (isAllowHashLineComments()) {
36813776
int hashIndex = matchedToken.image.indexOf('#');
36823777
if (hashIndex > 0) {
36833778
truncateIdentifierToken(matchedToken, hashIndex);
@@ -3728,8 +3823,8 @@ TOKEN:
37283823
// escapes the next character only with backslash escapes or in a PostgreSQL E'' string;
37293824
// otherwise the literal ends at the first quote that is not doubled, so LIKE ... ESCAPE '\\'
37303825
// still works.
3731-
boolean backslashEscapes = configuration.getAsBoolean(Feature.allowBackslashEscapeCharacter)
3732-
|| (AbstractJSqlParser.Dialect.POSTGRESQL.name().equals(configuration.getValue(Feature.dialect))
3826+
boolean backslashEscapes = isAllowBackslashEscapes()
3827+
|| (isPostgreSqlDialect()
37333828
&& matchedToken.image.regionMatches(true, 0, "E'", 0, 2));
37343829
int end = closingSingleQuote(matchedToken.image, backslashEscapes);
37353830
if (end > 0 && end < matchedToken.image.length()) {
@@ -3747,22 +3842,21 @@ TOKEN:
37473842
// the active quoting rules: a backslash escapes the next character only in
37483843
// double-quoted strings with backslash escapes, as in MySQL.
37493844
int end = closingQuote(matchedToken.image, 0, '"',
3750-
configuration.getAsBoolean(Feature.allowDoubleQuotedStrings)
3751-
&& configuration.getAsBoolean(Feature.allowBackslashEscapeCharacter));
3845+
isAllowDoubleQuotedStrings() && isAllowBackslashEscapes());
37523846
if (end > 0 && end < matchedToken.image.length()) {
37533847
matchedToken.image = image.substring(0, end);
37543848
input_stream.backup(image.length() - end);
37553849
}
37563850
}
3757-
if ( !configuration.getAsBoolean(Feature.allowSquareBracketQuotation)
3851+
if ( !isAllowSquareBracketQuotation()
37583852
&& matchedToken.image.charAt(0) == '[' ) {
37593853

37603854
matchedToken.image = "[";
37613855
// `squaredBracketOpenIndex` defined in TokenManagerDeclaration above
37623856
matchedToken.kind = squaredBracketOpenIndex;
37633857
input_stream.backup(image.length() - 1);
37643858
}
3765-
if ( configuration.getAsBoolean(Feature.allowDoubleQuotedStrings)
3859+
if ( isAllowDoubleQuotedStrings()
37663860
&& matchedToken.image.charAt(0) == '"' ) {
37673861
// `charLiteralIndex` defined in TokenManagerDeclaration above
37683862
matchedToken.kind = charLiteralIndex;
@@ -12019,7 +12113,7 @@ Expression PrimaryExpression() #PrimaryExpression:
1201912113
Token token = null;
1202012114
Token sign = null;
1202112115
Token innerSign;
12022-
List<Token> innerSigns = new ArrayList<Token>();
12116+
List<Token> innerSigns = null;
1202312117
ColDataType type = null;
1202412118
boolean not = false;
1202512119
boolean exclamationMarkNot = false;
@@ -12033,7 +12127,8 @@ Expression PrimaryExpression() #PrimaryExpression:
1203312127
[
1203412128
( sign="+" | sign="-" | sign="~" )
1203512129
// DuckDB's chained signs, e.g. - -1 or +-+1
12036-
( LOOKAHEAD(1) ( innerSign="+" | innerSign="-" | innerSign="~" ) { innerSigns.add(innerSign); } )*
12130+
( LOOKAHEAD(1) ( innerSign="+" | innerSign="-" | innerSign="~" )
12131+
{ if (innerSigns == null) { innerSigns = new ArrayList<Token>(); } innerSigns.add(innerSign); } )*
1203712132
]
1203812133
(
1203912134
<K_NULL> { retval = new NullValue(); }
@@ -12249,8 +12344,10 @@ Expression PrimaryExpression() #PrimaryExpression:
1224912344
timezoneExpr.setLeftExpression(retval);
1225012345
retval=timezoneExpr;
1225112346
}
12252-
for (int i = innerSigns.size() - 1; i >= 0; i--) {
12253-
retval = new SignedExpression(innerSigns.get(i).image.charAt(0), retval);
12347+
if (innerSigns != null) {
12348+
for (int i = innerSigns.size() - 1; i >= 0; i--) {
12349+
retval = new SignedExpression(innerSigns.get(i).image.charAt(0), retval);
12350+
}
1225412351
}
1225512352
if (sign != null) {
1225612353
retval = new SignedExpression(sign.image.charAt(0), retval);

0 commit comments

Comments
 (0)