From 0f40b3b69cbe3fd136453c26eb3f148c87f373ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BB=98=E5=85=B8?= Date: Thu, 27 Aug 2026 10:07:37 +0800 Subject: [PATCH] feat(parser): add BIGQUERY, DATABRICKS and SNOWFLAKE dialect presets Three warehouse presets for the lexer feature switches, each row taken from the vendor docs: BIGQUERY carries double quoted strings, # line comments, backslash escapes and the any-whitespace adjacent literal rule (GoogleSQL lexical structure; identifiers are backticked); DATABRICKS the same minus # comments (Spark literals; the double quoted identifiers switch is a non-default conf); SNOWFLAKE backslash escape sequences only, double quotes stay quoted identifiers (string and binary data types). Redshift was surveyed too and stays with the defaults on every switch, so it gains no preset entry. Follows up on the four warehouses named in #2512. --- .../jsqlparser/parser/AbstractJSqlParser.java | 10 +++- src/site/sphinx/usage.rst | 2 +- .../parser/CCJSqlParserUtilTest.java | 51 +++++++++++++++++-- 3 files changed, 56 insertions(+), 7 deletions(-) diff --git a/src/main/java/net/sf/jsqlparser/parser/AbstractJSqlParser.java b/src/main/java/net/sf/jsqlparser/parser/AbstractJSqlParser.java index 27880a2c7..cd4bfba7e 100644 --- a/src/main/java/net/sf/jsqlparser/parser/AbstractJSqlParser.java +++ b/src/main/java/net/sf/jsqlparser/parser/AbstractJSqlParser.java @@ -37,7 +37,15 @@ public enum Dialect { Feature.allowHashLineComments, Feature.allowDoubleQuotedStrings), SQLSERVER(AdjacentStringLiterals.OFF, Feature.allowSquareBracketQuotation), POSTGRESQL( - AdjacentStringLiterals.NEWLINE), H2, EXASOL; + AdjacentStringLiterals.NEWLINE), H2, EXASOL, BIGQUERY( + AdjacentStringLiterals.WHITESPACE, + Feature.allowDoubleQuotedStrings, + Feature.allowHashLineComments, + Feature.allowBackslashEscapeCharacter), DATABRICKS( + AdjacentStringLiterals.WHITESPACE, + Feature.allowDoubleQuotedStrings, + Feature.allowBackslashEscapeCharacter), SNOWFLAKE( + Feature.allowBackslashEscapeCharacter); private final Set lexerFeatures; private final AdjacentStringLiterals adjacentStringLiterals; diff --git a/src/site/sphinx/usage.rst b/src/site/sphinx/usage.rst index 3ad3190d9..e92173be9 100644 --- a/src/site/sphinx/usage.rst +++ b/src/site/sphinx/usage.rst @@ -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``, ``withHashLineComments`` and ``withDoubleQuotedStrings`` (MySQL and MariaDB syntax, the latter for the default sql_mode), ``withDialect(Dialect.SQLSERVER)`` turns on ``withSquareBracketQuotation``. ``withDialect(Dialect.POSTGRESQL)`` and ``withDialect(Dialect.ANSI_SQL)`` turn on the newline rule for adjacent String Literals. 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``. ``withDialect(Dialect.POSTGRESQL)`` and ``withDialect(Dialect.ANSI_SQL)`` turn on the newline rule for adjacent String Literals. ``withDialect(Dialect.BIGQUERY)`` and ``withDialect(Dialect.DATABRICKS)`` turn on ``withDoubleQuotedStrings`` and ``withBackslashEscapeCharacter`` plus the any-whitespace rule for adjacent String Literals, the BigQuery preset additionally ``withHashLineComments``; ``withDialect(Dialect.SNOWFLAKE)`` turns on ``withBackslashEscapeCharacter`` only, keeping double quotes as quoted identifiers. Features set explicitly after the dialect preset win over the preset. .. code-block:: java diff --git a/src/test/java/net/sf/jsqlparser/parser/CCJSqlParserUtilTest.java b/src/test/java/net/sf/jsqlparser/parser/CCJSqlParserUtilTest.java index 8add3b1bc..40e61e708 100644 --- a/src/test/java/net/sf/jsqlparser/parser/CCJSqlParserUtilTest.java +++ b/src/test/java/net/sf/jsqlparser/parser/CCJSqlParserUtilTest.java @@ -641,19 +641,51 @@ public void testDoubleQuotedStringsFeature() throws Exception { .parse("SELECT * FROM \"t\"", p -> p.withDoubleQuotedStrings(true)).toString()); } + @Test + public void testDialectPresetsWarehouses() throws Exception { + // BIGQUERY: double quoted strings, # line comments and backslash + // escapes (GoogleSQL "Lexical structure and syntax", identifiers are + // backticked), backticks stay unconditional + 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(AbstractJSqlParser.Dialect.BIGQUERY)) + .toString()); + // DATABRICKS: double quoted strings and backslash escapes (Spark + // "Literals"), `#` stays the #2507 operator + assertEquals("SELECT \"a\" FROM t WHERE x = 'it\\'s'", CCJSqlParserUtil + .parse("SELECT \"a\" FROM t WHERE x = 'it\\'s'", + p -> p.withDialect(AbstractJSqlParser.Dialect.DATABRICKS)) + .toString()); + assertEquals("SELECT 42 # 24", CCJSqlParserUtil.parse("SELECT 42 # 24", + p -> p.withDialect(AbstractJSqlParser.Dialect.DATABRICKS)).toString()); + // SNOWFLAKE: backslash escape sequences ("String & binary data + // types"), `#` stays the operator + assertEquals("SELECT col FROM t WHERE a = 'x\\'yz'", CCJSqlParserUtil + .parse("SELECT col FROM t WHERE a = 'x\\'yz'", + p -> p.withDialect(AbstractJSqlParser.Dialect.SNOWFLAKE)) + .toString()); + assertEquals("SELECT 42 # 24", CCJSqlParserUtil.parse("SELECT 42 # 24", + p -> p.withDialect(AbstractJSqlParser.Dialect.SNOWFLAKE)).toString()); + } + @Test public void testDoubleQuotedStringsPreset() throws Exception { - // MYSQL and MARIADB presets carry the switch (default sql_mode reading) + // the string-default dialects: MYSQL and MARIADB (default sql_mode), + // BIGQUERY and DATABRICKS (string literals, identifiers are backticked) for (AbstractJSqlParser.Dialect dialect : new AbstractJSqlParser.Dialect[] { - AbstractJSqlParser.Dialect.MYSQL, AbstractJSqlParser.Dialect.MARIADB}) { + AbstractJSqlParser.Dialect.MYSQL, AbstractJSqlParser.Dialect.MARIADB, + AbstractJSqlParser.Dialect.BIGQUERY, AbstractJSqlParser.Dialect.DATABRICKS}) { 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); + for (AbstractJSqlParser.Dialect dialect : new AbstractJSqlParser.Dialect[] { + AbstractJSqlParser.Dialect.SQLSERVER, AbstractJSqlParser.Dialect.SNOWFLAKE}) { + PlainSelect select = (PlainSelect) ((Select) CCJSqlParserUtil.parse("SELECT \"abc\"", + p -> p.withDialect(dialect))).getSelectBody(); + assertTrue(select.getSelectItems().get(0).getExpression() instanceof Column); + } } @Test @@ -717,6 +749,15 @@ public void testAdjacentStringLiteralsWhitespace() throws Exception { p -> p.withDoubleQuotedStrings(true).withAdjacentStringLiterals( AbstractJSqlParser.AdjacentStringLiterals.WHITESPACE)) .toString()); + // the BigQuery and Databricks presets carry it, the Snowflake preset + // does not (no adjacent-literal concatenation documented) + for (AbstractJSqlParser.Dialect dialect : new AbstractJSqlParser.Dialect[] { + AbstractJSqlParser.Dialect.BIGQUERY, AbstractJSqlParser.Dialect.DATABRICKS}) { + assertEquals("SELECT 'ab'", CCJSqlParserUtil.parse("SELECT 'a' 'b'", + p -> p.withDialect(dialect)).toString()); + } + assertEquals("SELECT 'a' 'b'", CCJSqlParserUtil.parse("SELECT 'a' 'b'", + p -> p.withDialect(AbstractJSqlParser.Dialect.SNOWFLAKE)).toString()); } @Test