Skip to content

Commit 0f911fa

Browse files
committed
fix: invalidate cached lexical features on configuration changes
Track configuration mutations as well as object identity, preserve the public token manager configuration, and avoid caching dynamic subclass values. Cover parser reuse, in-place changes, replacement, and direct lexer APIs with 33 regression checks. Signed-off-by: 付典 <fudianchn@gmail.com>
1 parent 643b2ad commit 0f911fa

4 files changed

Lines changed: 356 additions & 55 deletions

File tree

‎src/main/java/net/sf/jsqlparser/parser/SimpleCharStream.java‎

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -68,22 +68,24 @@ public class SimpleCharStream {
6868
String mySqlCommentError;
6969
boolean mySqlMinusSubstitution;
7070

71-
// derived from featureConfiguration, refreshed on identity change: the
72-
// configuration is wired before parsing starts and is not mutated mid-parse
71+
// ReInit retains the configuration, which callers can mutate between parses.
7372
private FeatureConfiguration derivedFrom;
73+
private long derivedModificationCount;
7474
private boolean derivedMySqlDialect;
7575
private boolean derivedBlankLineSeparator = true;
7676
private boolean derivedAllowHashLineComments;
7777

7878
private void refreshDerivedFeatures() {
79-
derivedFrom = featureConfiguration;
8079
derivedMySqlDialect = featureConfiguration != null
8180
&& AbstractJSqlParser.Dialect.MYSQL.name()
8281
.equals(featureConfiguration.getValue(Feature.dialect));
8382
derivedBlankLineSeparator = featureConfiguration == null
8483
|| featureConfiguration.getAsBoolean(Feature.allowBlankLinesAsStatementSeparator);
8584
derivedAllowHashLineComments = featureConfiguration != null
8685
&& featureConfiguration.getAsBoolean(Feature.allowHashLineComments);
86+
derivedFrom = featureConfiguration;
87+
derivedModificationCount = featureConfiguration == null ? 0
88+
: featureConfiguration.getModificationCount();
8789
}
8890

8991
/**
@@ -219,7 +221,9 @@ public final char BeginToken() throws java.io.IOException {
219221

220222
absoluteTokenBegin = totalCharsRead;
221223

222-
if (featureConfiguration != derivedFrom) {
224+
if (featureConfiguration != derivedFrom || featureConfiguration != null
225+
&& (featureConfiguration.getModificationCount() != derivedModificationCount
226+
|| featureConfiguration.getClass() != FeatureConfiguration.class)) {
223227
refreshDerivedFeatures();
224228
}
225229

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ public class FeatureConfiguration {
2020
private static final Logger LOG = Logger.getLogger(FeatureConfiguration.class.getName());
2121

2222
private final Map<Feature, Object> featureEnabled = new EnumMap<>(Feature.class);
23+
private long modificationCount;
2324

2425
public FeatureConfiguration() {
2526
// set default-value for all switchable features
@@ -35,6 +36,7 @@ public FeatureConfiguration() {
3536
public FeatureConfiguration setValue(Feature feature, Object value) {
3637
if (feature.isConfigurable()) {
3738
featureEnabled.put(feature, value);
39+
modificationCount++;
3840
} else {
3941
if (LOG.isLoggable(Level.WARNING)) {
4042
LOG.warning(feature.name() + " is not switchable - cannot set enabled = " + value);
@@ -43,6 +45,11 @@ public FeatureConfiguration setValue(Feature feature, Object value) {
4345
return this;
4446
}
4547

48+
/** Returns a change stamp for caches of derived feature values. */
49+
public long getModificationCount() {
50+
return modificationCount;
51+
}
52+
4653
/**
4754
* @param feature
4855
* @return the configured feature value - can be <code>null</code>

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

Lines changed: 23 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -2713,92 +2713,64 @@ PARSER_END(CCJSqlParser)
27132713
TOKEN_MGR_DECLS : {
27142714
public FeatureConfiguration configuration = new FeatureConfiguration();
27152715

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.
2716+
// The public configuration can be replaced or mutated, including between ReInit calls.
2717+
// Subclasses may provide dynamic values without going through setValue.
27202718
private FeatureConfiguration lexicalConfiguration;
2719+
private long lexicalModificationCount;
27212720
private boolean lexicalPostgreSqlDialect;
27222721
private boolean lexicalBigQueryDialect;
27232722
private boolean lexicalAllowHashLineComments;
27242723
private boolean lexicalAllowBackslashEscapes;
27252724
private boolean lexicalAllowDoubleQuotedStrings;
27262725
private boolean lexicalAllowSquareBracketQuotation;
27272726

2728-
private void refreshLexicalConfiguration(FeatureConfiguration wired) {
2729-
lexicalConfiguration = wired;
2730-
Object dialect = wired.getValue(Feature.dialect);
2727+
private void refreshLexicalConfiguration() {
2728+
long modificationCount = configuration.getModificationCount();
2729+
if (configuration == lexicalConfiguration && modificationCount == lexicalModificationCount
2730+
&& configuration.getClass() == FeatureConfiguration.class) {
2731+
return;
2732+
}
2733+
Object dialect = configuration.getValue(Feature.dialect);
27312734
lexicalPostgreSqlDialect = AbstractJSqlParser.Dialect.POSTGRESQL.name().equals(dialect);
27322735
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);
2736+
lexicalAllowHashLineComments = Boolean.TRUE.equals(configuration.getValue(Feature.allowHashLineComments));
2737+
lexicalAllowBackslashEscapes = configuration.getAsBoolean(Feature.allowBackslashEscapeCharacter);
2738+
lexicalAllowDoubleQuotedStrings = configuration.getAsBoolean(Feature.allowDoubleQuotedStrings);
2739+
lexicalAllowSquareBracketQuotation = configuration.getAsBoolean(Feature.allowSquareBracketQuotation);
2740+
lexicalConfiguration = configuration;
2741+
lexicalModificationCount = modificationCount;
27372742
}
27382743

27392744
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-
}
2745+
refreshLexicalConfiguration();
27472746
return lexicalPostgreSqlDialect;
27482747
}
27492748

27502749
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-
}
2750+
refreshLexicalConfiguration();
27582751
return lexicalBigQueryDialect;
27592752
}
27602753

27612754
private boolean isAllowHashLineComments() {
2762-
FeatureConfiguration wired = input_stream.featureConfiguration;
2763-
if (wired == null) {
2755+
if (input_stream.featureConfiguration == null) {
27642756
return false;
27652757
}
2766-
if (wired != lexicalConfiguration) {
2767-
refreshLexicalConfiguration(wired);
2768-
}
2758+
refreshLexicalConfiguration();
27692759
return lexicalAllowHashLineComments;
27702760
}
27712761

27722762
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-
}
2763+
refreshLexicalConfiguration();
27802764
return lexicalAllowBackslashEscapes;
27812765
}
27822766

27832767
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-
}
2768+
refreshLexicalConfiguration();
27912769
return lexicalAllowDoubleQuotedStrings;
27922770
}
27932771

27942772
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-
}
2773+
refreshLexicalConfiguration();
28022774
return lexicalAllowSquareBracketQuotation;
28032775
}
28042776

0 commit comments

Comments
 (0)