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
6 changes: 6 additions & 0 deletions src/main/java/net/sf/jsqlparser/expression/StringValue.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,12 @@ public StringValue(String escapedValue) {
&& escapedValue.endsWith("'")) {
value = escapedValue.substring(1, escapedValue.length() - 1);
return;
} else if (escapedValue.length() >= 2 && escapedValue.startsWith("\"")
&& escapedValue.endsWith("\"")) {
// double quoted String Literals (Feature.allowDoubleQuotedStrings)
value = escapedValue.substring(1, escapedValue.length() - 1);
quoteStr = "\"";
return;
} else if (escapedValue.length() >= 4 && escapedValue.startsWith("$$")
&& escapedValue.endsWith("$$")) {
value = escapedValue.substring(2, escapedValue.length() - 2);
Expand Down
14 changes: 12 additions & 2 deletions src/main/java/net/sf/jsqlparser/parser/AbstractJSqlParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,10 @@ public abstract class AbstractJSqlParser<P> {

public enum Dialect {
ANSI_SQL, ORACLE, MYSQL(Feature.allowBackslashEscapeCharacter,
Feature.allowHashLineComments), MARIADB(Feature.allowBackslashEscapeCharacter,
Feature.allowHashLineComments), SQLSERVER(
Feature.allowHashLineComments,
Feature.allowDoubleQuotedStrings), MARIADB(Feature.allowBackslashEscapeCharacter,
Feature.allowHashLineComments,
Feature.allowDoubleQuotedStrings), SQLSERVER(
Feature.allowSquareBracketQuotation), POSTGRESQL, H2, EXASOL;

private final Set<Feature> lexerFeatures;
Expand Down Expand Up @@ -98,6 +100,14 @@ public P withBackslashEscapeCharacter(boolean allowBackslashEscapeCharacter) {
return withFeature(Feature.allowBackslashEscapeCharacter, allowBackslashEscapeCharacter);
}

public P withDoubleQuotedStrings() {
return withFeature(Feature.allowDoubleQuotedStrings, true);
}

public P withDoubleQuotedStrings(boolean allowDoubleQuotedStrings) {
return withFeature(Feature.allowDoubleQuotedStrings, allowDoubleQuotedStrings);
}

public P withHashLineComments() {
return withFeature(Feature.allowHashLineComments, true);
}
Expand Down
6 changes: 6 additions & 0 deletions src/main/java/net/sf/jsqlparser/parser/feature/Feature.java
Original file line number Diff line number Diff line change
Expand Up @@ -795,6 +795,12 @@ public enum Feature {
*/
allowBackslashEscapeCharacter(false),

/**
* allows double quoted String Literals (BigQuery, Spark/Databricks, MySQL default sql_mode);
* disabled by default, where double quotes stay quoted identifiers (ANSI SQL)
*/
allowDoubleQuotedStrings(false),

/**
* allows MySQL `#` line comments; disabled by default, where a lone `#` stays the binary
* operator (#2507: PostgreSQL bitwise XOR / geometric intersection)
Expand Down
5 changes: 5 additions & 0 deletions src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt
Original file line number Diff line number Diff line change
Expand Up @@ -2077,6 +2077,11 @@ TOKEN:
matchedToken.kind = squaredBracketOpenIndex;
input_stream.backup(image.length() - 1);
}
if ( configuration.getAsBoolean(Feature.allowDoubleQuotedStrings)
&& matchedToken.image.charAt(0) == '"' ) {
// `charLiteralIndex` defined in TokenManagerDeclaration above
matchedToken.kind = charLiteralIndex;
}
}
}

Expand Down
4 changes: 2 additions & 2 deletions src/site/sphinx/usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,7 @@ Define the Parser Features

JSQLParser interprets Squared Brackets ``[..]`` as Arrays, which does not work with MS SQL Server and T-SQL. Please use the Parser Features to instruct JSQLParser to read Squared Brackets as Quotes instead.

JSQLParser allows for standard compliant Single Quote ``'..`` Escaping. Additional Back-slash ``\..`` Escaping needs to be activated by setting the ``BackSlashEscapeCharacter`` parser feature.
JSQLParser allows for standard compliant Single Quote ``'..`` Escaping. Additional Back-slash ``\..`` Escaping needs to be activated by setting the ``BackSlashEscapeCharacter`` parser feature. JSQLParser reads Double Quotes ``".."`` as quoted identifiers (ANSI SQL); reading them as String Literals (BigQuery, Spark/Databricks, MySQL default sql_mode) needs the ``DoubleQuotedStrings`` parser feature.

Additionally there are Features to control the Parser's effort at the cost of the performance.

Expand Down Expand Up @@ -319,7 +319,7 @@ Additionally there are Features to control the Parser's effort at the cost of th
.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.
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``, ``withHashLineComments`` and ``withDoubleQuotedStrings`` (MySQL and MariaDB syntax, the latter for the default sql_mode), ``withDialect(Dialect.SQLSERVER)`` turns on ``withSquareBracketQuotation``. Features set explicitly after the dialect preset win over the preset.

.. code-block:: java

Expand Down
44 changes: 44 additions & 0 deletions src/test/java/net/sf/jsqlparser/parser/CCJSqlParserUtilTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.LongValue;
import net.sf.jsqlparser.expression.StringValue;
import net.sf.jsqlparser.expression.operators.arithmetic.Addition;
import net.sf.jsqlparser.expression.operators.arithmetic.Multiplication;
import net.sf.jsqlparser.expression.operators.relational.ParenthesedExpressionList;
Expand All @@ -20,6 +21,7 @@
import net.sf.jsqlparser.statement.Statements;
import net.sf.jsqlparser.statement.UnsupportedStatement;
import net.sf.jsqlparser.statement.select.PlainSelect;
import net.sf.jsqlparser.statement.select.Select;
import net.sf.jsqlparser.statement.select.TableStatement;
import net.sf.jsqlparser.test.MemoryLeakVerifier;
import net.sf.jsqlparser.test.TestUtils;
Expand All @@ -44,6 +46,7 @@
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;

public class CCJSqlParserUtilTest {

Expand Down Expand Up @@ -611,4 +614,45 @@ public void testDialectPresetSwitchOverrides() throws Exception {
.withDialect(AbstractJSqlParser.Dialect.MYSQL))
.toString());
}

@Test
public void testDoubleQuotedStringsFeature() throws Exception {
// default (flag off): double quotes are quoted identifiers, unchanged
PlainSelect select = (PlainSelect) ((Select) CCJSqlParserUtil
.parse("SELECT \"not an identifier\"")).getSelectBody();
assertTrue(select.getSelectItems().get(0).getExpression() instanceof Column);
// on: the BigQuery/Spark/MySQL-default reading, double quotes are
// string literals, the quote kept on round-trip
select = (PlainSelect) ((Select) CCJSqlParserUtil.parse("SELECT \"not an identifier\"",
p -> p.withDoubleQuotedStrings(true))).getSelectBody();
Expression expression = select.getSelectItems().get(0).getExpression();
assertTrue(expression instanceof StringValue);
assertEquals("not an identifier", ((StringValue) expression).getValue());
assertEquals("SELECT \"not an identifier\"", select.toString());
// empty string and doubled quotes take the single-quote treatment
assertEquals("SELECT \"\"", CCJSqlParserUtil
.parse("SELECT \"\"", p -> p.withDoubleQuotedStrings(true)).toString());
assertEquals("SELECT \"a\"\"b\"", CCJSqlParserUtil
.parse("SELECT \"a\"\"b\"", p -> p.withDoubleQuotedStrings(true)).toString());
// identifier positions: a `"..."` token follows the existing
// string-as-table branch (`FROM 'file.csv'`), the same leniency
// single quotes already have; MySQL itself would error here
assertEquals("SELECT * FROM \"t\"", CCJSqlParserUtil
.parse("SELECT * FROM \"t\"", p -> p.withDoubleQuotedStrings(true)).toString());
}

@Test
public void testDoubleQuotedStringsPreset() throws Exception {
// MYSQL and MARIADB presets carry the switch (default sql_mode reading)
for (AbstractJSqlParser.Dialect dialect : new AbstractJSqlParser.Dialect[] {
AbstractJSqlParser.Dialect.MYSQL, AbstractJSqlParser.Dialect.MARIADB}) {
PlainSelect select = (PlainSelect) ((Select) CCJSqlParserUtil
.parse("SELECT \"abc\"", p -> p.withDialect(dialect))).getSelectBody();
assertTrue(select.getSelectItems().get(0).getExpression() instanceof StringValue);
}
// the identifier-default dialects keep the quoted identifier reading
PlainSelect select = (PlainSelect) ((Select) CCJSqlParserUtil.parse("SELECT \"abc\"",
p -> p.withDialect(AbstractJSqlParser.Dialect.SQLSERVER))).getSelectBody();
assertTrue(select.getSelectItems().get(0).getExpression() instanceof Column);
}
}
Loading