diff --git a/src/main/java/net/sf/jsqlparser/parser/AbstractJSqlParser.java b/src/main/java/net/sf/jsqlparser/parser/AbstractJSqlParser.java index b4e63851d..8d4e72ea4 100644 --- a/src/main/java/net/sf/jsqlparser/parser/AbstractJSqlParser.java +++ b/src/main/java/net/sf/jsqlparser/parser/AbstractJSqlParser.java @@ -13,7 +13,10 @@ import net.sf.jsqlparser.parser.feature.FeatureConfiguration; import java.util.ArrayList; +import java.util.Arrays; +import java.util.EnumSet; import java.util.List; +import java.util.Set; public abstract class AbstractJSqlParser

{ @@ -22,7 +25,21 @@ public abstract class AbstractJSqlParser

{ protected List parseErrors = new ArrayList<>(); public enum Dialect { - ORACLE, EXASOL + ANSI_SQL, ORACLE, MYSQL(Feature.allowBackslashEscapeCharacter, + Feature.allowHashLineComments), MARIADB(Feature.allowBackslashEscapeCharacter, + Feature.allowHashLineComments), SQLSERVER( + Feature.allowSquareBracketQuotation), POSTGRESQL, H2, EXASOL; + + private final Set lexerFeatures; + + Dialect(Feature... lexerFeatures) { + this.lexerFeatures = lexerFeatures.length == 0 ? EnumSet.noneOf(Feature.class) + : EnumSet.copyOf(Arrays.asList(lexerFeatures)); + } + + public Set getLexerFeatures() { + return lexerFeatures; + } } public P withSquareBracketQuotation() { @@ -62,7 +79,11 @@ public P withTimeOut(long timeOutMillSeconds) { } public P withDialect(Dialect dialect) { - return withFeature(Feature.dialect, dialect.name()); + withFeature(Feature.dialect, dialect.name()); + for (Feature lexerFeature : dialect.getLexerFeatures()) { + withFeature(lexerFeature, true); + } + return me(); } public P withAllowedNestingDepth(int allowedNestingDepth) { diff --git a/src/site/sphinx/usage.rst b/src/site/sphinx/usage.rst index c0bcdac88..d989b010b 100644 --- a/src/site/sphinx/usage.rst +++ b/src/site/sphinx/usage.rst @@ -317,4 +317,16 @@ Additionally there are Features to control the Parser's effort at the cost of th sqlStr , parser -> parser .withBackslashEscapeCharacter(true) - ); \ No newline at end of file + ); + +Instead of turning the individual Parser Features on one by one, a ``Dialect`` preset selects the features of that database dialect: ``withDialect(Dialect.MYSQL)`` turns on ``withBackslashEscapeCharacter`` and ``withHashLineComments`` (both MySQL and MariaDB syntax), ``withDialect(Dialect.SQLSERVER)`` turns on ``withSquareBracketQuotation``. Features set explicitly after the dialect preset win over the preset. + +.. code-block:: java + + // Select the Database Dialect: turns on that dialect's parser features + sqlStr="SELECT `col` FROM t WHERE a = 'x\\'yz' AND b = 42#24"; + Statement stmt3 = CCJSqlParserUtil.parse( + sqlStr + , parser -> parser + .withDialect(Dialect.MYSQL) + ); diff --git a/src/test/java/net/sf/jsqlparser/parser/CCJSqlParserUtilTest.java b/src/test/java/net/sf/jsqlparser/parser/CCJSqlParserUtilTest.java index 4a7258707..6addd7f83 100644 --- a/src/test/java/net/sf/jsqlparser/parser/CCJSqlParserUtilTest.java +++ b/src/test/java/net/sf/jsqlparser/parser/CCJSqlParserUtilTest.java @@ -564,4 +564,51 @@ public void testHashLineCommentsMySQLStatementSemantics() throws Exception { assertEquals("SELECT 1 # comment", CCJSqlParserUtil.parse("SELECT 1 # comment").toString()); } + + @Test + public void testDialectPresets() throws Exception { + // MYSQL and MARIADB: backslash escapes and # line comments (MySQL 8 + // manual "Comments" and "String Literals", MariaDB KB "Comment Syntax" + // and "String Literals"); backticks are always on, brackets are not + // MySQL syntax + for (AbstractJSqlParser.Dialect dialect : new AbstractJSqlParser.Dialect[] { + AbstractJSqlParser.Dialect.MYSQL, AbstractJSqlParser.Dialect.MARIADB}) { + assertEquals("SELECT `col` FROM t WHERE a = 'x\\'yz' AND b = 42", CCJSqlParserUtil + .parse("SELECT `col` FROM t WHERE a = 'x\\'yz' AND b = 42#24", + p -> p.withDialect(dialect)) + .toString()); + } + // SQLSERVER: bracket quotation only (Microsoft Learn "Database + // Identifiers"), `#` stays available for identifiers (temp tables) + assertEquals("SELECT [my column] FROM t", + CCJSqlParserUtil.parse("SELECT [my column] FROM t", + p -> p.withDialect(AbstractJSqlParser.Dialect.SQLSERVER)).toString()); + // empty presets keep the defaults: the #2507 operator, no backslash + // escapes + for (AbstractJSqlParser.Dialect dialect : new AbstractJSqlParser.Dialect[] { + AbstractJSqlParser.Dialect.ANSI_SQL, AbstractJSqlParser.Dialect.ORACLE, + AbstractJSqlParser.Dialect.POSTGRESQL, AbstractJSqlParser.Dialect.H2, + AbstractJSqlParser.Dialect.EXASOL}) { + assertEquals("SELECT 42 # 24", + CCJSqlParserUtil.parse("SELECT 42 # 24", p -> p.withDialect(dialect)) + .toString()); + assertThrows(JSQLParserException.class, () -> CCJSqlParserUtil + .parse("SELECT 'a\\'b'", p -> p.withDialect(dialect))); + } + } + + @Test + public void testDialectPresetSwitchOverrides() throws Exception { + // an explicit switch after the preset wins: `#` is the #2507 + // operator again + assertEquals("SELECT 1 # comment", CCJSqlParserUtil.parse("SELECT 1 # comment", + p -> p.withDialect(AbstractJSqlParser.Dialect.MYSQL).withHashLineComments(false)) + .toString()); + // the preset never turns a previously enabled switch off + assertEquals("SELECT [my column] FROM mytable", + CCJSqlParserUtil.parse("SELECT [my column] FROM mytable", + p -> p.withSquareBracketQuotation(true) + .withDialect(AbstractJSqlParser.Dialect.MYSQL)) + .toString()); + } }