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
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,11 @@ public P withHashLineComments(boolean allowHashLineComments) {
return withFeature(Feature.allowHashLineComments, allowHashLineComments);
}

public P withBlankLinesAsSeparator(boolean allowBlankLinesAsStatementSeparator) {
return withFeature(Feature.allowBlankLinesAsStatementSeparator,
allowBlankLinesAsStatementSeparator);
}

public P withDigitSeparators() {
return withFeature(Feature.allowDigitSeparators, true);
}
Expand Down
15 changes: 12 additions & 3 deletions src/main/java/net/sf/jsqlparser/parser/SimpleCharStream.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@
/* Generated By:JavaCC: Do not edit this line by hand. SimpleCharStream.java Version 8.0.0 */
/*
* Warning: this file is maintained by hand on top of the JavaCC template. Among other manual
* adjustments it carries the in-buffer '#' rewrite in BeginToken(), which
* Feature.allowHashLineComments depends on. Never overwrite it with a freshly generated
* SimpleCharStream, that would silently drop the rewrite and break allowHashLineComments.
* adjustments it carries the in-buffer '#' and blank line rewrites in BeginToken(), which
* Feature.allowHashLineComments and Feature.allowBlankLinesAsStatementSeparator depend on. Never
* overwrite it with a freshly generated SimpleCharStream, that would silently drop the rewrites and
* break both features.
*/
package net.sf.jsqlparser.parser;

Expand Down Expand Up @@ -208,6 +209,14 @@ public final char BeginToken() throws java.io.IOException {
&& AbstractJSqlParser.Dialect.MYSQL.name()
.equals(featureConfiguration.getValue(Feature.dialect))) {
c = mySqlCommentBoundary(c);
} else if (c == '\n' && featureConfiguration != null
&& !featureConfiguration
.getAsBoolean(Feature.allowBlankLinesAsStatementSeparator)
&& peekCharacters(2).equals("\n\n")) {
// Blanking the first newline of the run stops ST_SEMICOLON matching "\n\n\n";
// line and column tracking was already updated when the newline was read.
buffer[bufpos] = ' ';
c = ' ';
}
return c;
}
Expand Down
7 changes: 7 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 @@ -889,6 +889,13 @@ public enum Feature {
*/
allowHashLineComments(false),

/**
* treats three consecutive newlines as a statement separator (enabled by default). When
* disabled, blank lines are plain whitespace, so a statement containing them is parsed whole
* instead of ending, or failing to parse, at the first run of blank lines
*/
allowBlankLinesAsStatementSeparator(true),

/**
* allows digit separators in numeric literals, e.g. {@code 1_000_000} (DuckDB, PostgreSQL 16);
* disabled by default, where MySQL reads {@code 2024_01} as an identifier
Expand Down
3 changes: 3 additions & 0 deletions src/site/sphinx/usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,7 @@ For several statements at once, use ``CCJSqlParserUtil.parseStatements()``, whic
.. note::

Supported statement separators are semicolon ``;``, ``GO``, slash ``/`` and two empty lines ``\n\n\n``.
Use ``withBlankLinesAsSeparator(false)`` to treat empty lines as plain whitespace instead.

If parsing fails on syntax JSQLParser does not know, see :ref:`Handle Parse Errors` — and please `open an issue <https://github.com/JSQLParser/JSqlParser/issues>`_, missing syntax gets added on demand.

Expand Down Expand Up @@ -1571,6 +1572,8 @@ The individual features
- ``".."`` reads as a string literal instead of a quoted identifier (BigQuery, Spark/Databricks, MySQL default ``sql_mode``)
* - ``withHashLineComments``
- ``#`` starts a line comment
* - ``withBlankLinesAsSeparator``
- ``false`` stops two empty lines (``\n\n\n``) ending a statement, so they read as plain whitespace
* - ``withAdjacentStringLiterals``
- adjacent string literals concatenate: ``NEWLINE`` (SQL standard, PostgreSQL) or ``WHITESPACE`` (GoogleSQL, Spark/Databricks); ``true`` selects ``NEWLINE``, ``false`` switches it off
* - ``withAllowComplexParsing``
Expand Down
39 changes: 39 additions & 0 deletions src/test/java/net/sf/jsqlparser/parser/CCJSqlParserUtilTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer;

import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
Expand Down Expand Up @@ -578,6 +579,44 @@ void testSingleStatementWithEmptyLines() throws JSQLParserException {
+ "where id=?", true);
}

@Test
void testBlankLinesAsSeparatorDisabled() throws JSQLParserException {
Consumer<CCJSqlParser> blankLinesAsWhitespace = p -> p.withBlankLinesAsSeparator(false);

assertEquals("SELECT a FROM dual WHERE x = ? AND y = ?",
CCJSqlParserUtil.parse("SELECT a FROM dual WHERE x = ?\n\n\n AND y = ?",
blankLinesAsWhitespace).toString());
assertEquals("SELECT a, b FROM t",
CCJSqlParserUtil.parse("SELECT a,\n\n\n\nb FROM t", blankLinesAsWhitespace)
.toString());
assertEquals("SELECT a FROM t WHERE a > 1",
CCJSqlParserUtil.parse("SELECT a FROM t\n\n\nWHERE a > 1", blankLinesAsWhitespace)
.toString());
assertEquals("SELECT a FROM t UNION SELECT a FROM u",
CCJSqlParserUtil.parse("SELECT a FROM t\n\n\n\n\nUNION\n\n\nSELECT a FROM u",
blankLinesAsWhitespace).toString());
assertEquals("SELECT a FROM t JOIN u ON t.id = u.id",
CCJSqlParserUtil.parse("SELECT a FROM t\n\n\nJOIN u ON t.id = u.id",
blankLinesAsWhitespace).toString());
assertEquals("SELECT a FROM dual",
CCJSqlParserUtil.parse("SELECT a FROM dual\n\n\n", blankLinesAsWhitespace)
.toString());
assertEquals("SELECT 'a\n\n\nb' FROM dual",
CCJSqlParserUtil.parse("SELECT 'a\n\n\nb' FROM dual", blankLinesAsWhitespace)
.toString());

assertEquals(1, CCJSqlParserUtil.parseStatements(
"SELECT a FROM t\n\n\nWHERE a > 1", blankLinesAsWhitespace).size());
assertEquals(3, CCJSqlParserUtil.parseStatements(
"SELECT 1 FROM dual;\n\n\nSELECT 2 FROM dual\ngo\nSELECT 3 FROM dual\n/\n",
blankLinesAsWhitespace).size());

JSQLParserException exception = assertThrows(JSQLParserException.class,
() -> CCJSqlParserUtil.parse("SELECT a\n\n\n\nFROM", blankLinesAsWhitespace));
assertTrue(exception.getCause().getMessage().contains("at line 5, column 1"),
exception.getCause().getMessage());
}

@Test
public void testHashLineCommentsFeature() throws Exception {
// with the flag: MySQL line comments, `#` to end of line,
Expand Down
Loading