Skip to content

Commit d58aee4

Browse files
committed
feat(parser): enable the dialect's lexer feature preset in withDialect
Extends Dialect with ANSI_SQL, MYSQL, MARIADB, SQLSERVER, POSTGRESQL and H2 and makes withDialect turn on the dialect's lexer switches on top of the grammar gating: backslash escapes and # line comments for MYSQL and MARIADB, square bracket quotation for SQLSERVER, none for the rest. The preset only turns features on, an explicit switch after it still wins. Implements #2510.
1 parent 177dbd7 commit d58aee4

2 files changed

Lines changed: 70 additions & 2 deletions

File tree

src/main/java/net/sf/jsqlparser/parser/AbstractJSqlParser.java

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,10 @@
1313
import net.sf.jsqlparser.parser.feature.FeatureConfiguration;
1414

1515
import java.util.ArrayList;
16+
import java.util.Arrays;
17+
import java.util.EnumSet;
1618
import java.util.List;
19+
import java.util.Set;
1720

1821
public abstract class AbstractJSqlParser<P> {
1922

@@ -22,7 +25,21 @@ public abstract class AbstractJSqlParser<P> {
2225
protected List<ParseException> parseErrors = new ArrayList<>();
2326

2427
public enum Dialect {
25-
ORACLE, EXASOL
28+
ANSI_SQL, ORACLE, MYSQL(Feature.allowBackslashEscapeCharacter,
29+
Feature.allowHashLineComments), MARIADB(Feature.allowBackslashEscapeCharacter,
30+
Feature.allowHashLineComments), SQLSERVER(
31+
Feature.allowSquareBracketQuotation), POSTGRESQL, H2, EXASOL;
32+
33+
private final Set<Feature> lexerFeatures;
34+
35+
Dialect(Feature... lexerFeatures) {
36+
this.lexerFeatures = lexerFeatures.length == 0 ? EnumSet.noneOf(Feature.class)
37+
: EnumSet.copyOf(Arrays.asList(lexerFeatures));
38+
}
39+
40+
public Set<Feature> getLexerFeatures() {
41+
return lexerFeatures;
42+
}
2643
}
2744

2845
public P withSquareBracketQuotation() {
@@ -62,7 +79,11 @@ public P withTimeOut(long timeOutMillSeconds) {
6279
}
6380

6481
public P withDialect(Dialect dialect) {
65-
return withFeature(Feature.dialect, dialect.name());
82+
withFeature(Feature.dialect, dialect.name());
83+
for (Feature lexerFeature : dialect.getLexerFeatures()) {
84+
withFeature(lexerFeature, true);
85+
}
86+
return me();
6687
}
6788

6889
public P withAllowedNestingDepth(int allowedNestingDepth) {

src/test/java/net/sf/jsqlparser/parser/CCJSqlParserUtilTest.java

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -564,4 +564,51 @@ public void testHashLineCommentsMySQLStatementSemantics() throws Exception {
564564
assertEquals("SELECT 1 # comment",
565565
CCJSqlParserUtil.parse("SELECT 1 # comment").toString());
566566
}
567+
568+
@Test
569+
public void testDialectPresets() throws Exception {
570+
// MYSQL and MARIADB: backslash escapes and # line comments (MySQL 8
571+
// manual "Comments" and "String Literals", MariaDB KB "Comment Syntax"
572+
// and "String Literals"); backticks are always on, brackets are not
573+
// MySQL syntax
574+
for (AbstractJSqlParser.Dialect dialect : new AbstractJSqlParser.Dialect[] {
575+
AbstractJSqlParser.Dialect.MYSQL, AbstractJSqlParser.Dialect.MARIADB}) {
576+
assertEquals("SELECT `col` FROM t WHERE a = 'x\\'yz' AND b = 42", CCJSqlParserUtil
577+
.parse("SELECT `col` FROM t WHERE a = 'x\\'yz' AND b = 42#24",
578+
p -> p.withDialect(dialect))
579+
.toString());
580+
}
581+
// SQLSERVER: bracket quotation only (Microsoft Learn "Database
582+
// Identifiers"), `#` stays available for identifiers (temp tables)
583+
assertEquals("SELECT [my column] FROM t",
584+
CCJSqlParserUtil.parse("SELECT [my column] FROM t",
585+
p -> p.withDialect(AbstractJSqlParser.Dialect.SQLSERVER)).toString());
586+
// empty presets keep the defaults: the #2507 operator, no backslash
587+
// escapes
588+
for (AbstractJSqlParser.Dialect dialect : new AbstractJSqlParser.Dialect[] {
589+
AbstractJSqlParser.Dialect.ANSI_SQL, AbstractJSqlParser.Dialect.ORACLE,
590+
AbstractJSqlParser.Dialect.POSTGRESQL, AbstractJSqlParser.Dialect.H2,
591+
AbstractJSqlParser.Dialect.EXASOL}) {
592+
assertEquals("SELECT 42 # 24",
593+
CCJSqlParserUtil.parse("SELECT 42 # 24", p -> p.withDialect(dialect))
594+
.toString());
595+
assertThrows(JSQLParserException.class, () -> CCJSqlParserUtil
596+
.parse("SELECT 'a\\'b'", p -> p.withDialect(dialect)));
597+
}
598+
}
599+
600+
@Test
601+
public void testDialectPresetSwitchOverrides() throws Exception {
602+
// an explicit switch after the preset wins: `#` is the #2507
603+
// operator again
604+
assertEquals("SELECT 1 # comment", CCJSqlParserUtil.parse("SELECT 1 # comment",
605+
p -> p.withDialect(AbstractJSqlParser.Dialect.MYSQL).withHashLineComments(false))
606+
.toString());
607+
// the preset never turns a previously enabled switch off
608+
assertEquals("SELECT [my column] FROM mytable",
609+
CCJSqlParserUtil.parse("SELECT [my column] FROM mytable",
610+
p -> p.withSquareBracketQuotation(true)
611+
.withDialect(AbstractJSqlParser.Dialect.MYSQL))
612+
.toString());
613+
}
567614
}

0 commit comments

Comments
 (0)