Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 23 additions & 2 deletions src/main/java/net/sf/jsqlparser/parser/AbstractJSqlParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -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<P> {

Expand All @@ -22,7 +25,21 @@ public abstract class AbstractJSqlParser<P> {
protected List<ParseException> 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<Feature> lexerFeatures;

Dialect(Feature... lexerFeatures) {
this.lexerFeatures = lexerFeatures.length == 0 ? EnumSet.noneOf(Feature.class)
: EnumSet.copyOf(Arrays.asList(lexerFeatures));
}

public Set<Feature> getLexerFeatures() {
return lexerFeatures;
}
}

public P withSquareBracketQuotation() {
Expand Down Expand Up @@ -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) {
Expand Down
14 changes: 13 additions & 1 deletion src/site/sphinx/usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -317,4 +317,16 @@ Additionally there are Features to control the Parser's effort at the cost of th
sqlStr
, parser -> parser
.withBackslashEscapeCharacter(true)
);
);

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)
);
47 changes: 47 additions & 0 deletions src/test/java/net/sf/jsqlparser/parser/CCJSqlParserUtilTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}
}
Loading