Skip to content

Commit 8f0bd0c

Browse files
edeadingtonclaude
andauthored
Add an option to stop treating blank lines as a statement separator (#2800)
* Add an option to stop treating blank lines as a statement separator Three consecutive newlines end a statement, like `;`. Add Feature.allowBlankLinesAsStatementSeparator (enabled by default) and withBlankLinesAsSeparator(boolean) so callers can parse blank lines as plain whitespace instead. Default behaviour is unchanged. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * Document the blank line separator option Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * Reword the blank line separator option docs for parse failures Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
1 parent f7d84c8 commit 8f0bd0c

5 files changed

Lines changed: 66 additions & 3 deletions

File tree

‎src/main/java/net/sf/jsqlparser/parser/AbstractJSqlParser.java‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,11 @@ public P withHashLineComments(boolean allowHashLineComments) {
195195
return withFeature(Feature.allowHashLineComments, allowHashLineComments);
196196
}
197197

198+
public P withBlankLinesAsSeparator(boolean allowBlankLinesAsStatementSeparator) {
199+
return withFeature(Feature.allowBlankLinesAsStatementSeparator,
200+
allowBlankLinesAsStatementSeparator);
201+
}
202+
198203
public P withDigitSeparators() {
199204
return withFeature(Feature.allowDigitSeparators, true);
200205
}

‎src/main/java/net/sf/jsqlparser/parser/SimpleCharStream.java‎

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,10 @@
1010
/* Generated By:JavaCC: Do not edit this line by hand. SimpleCharStream.java Version 8.0.0 */
1111
/*
1212
* Warning: this file is maintained by hand on top of the JavaCC template. Among other manual
13-
* adjustments it carries the in-buffer '#' rewrite in BeginToken(), which
14-
* Feature.allowHashLineComments depends on. Never overwrite it with a freshly generated
15-
* SimpleCharStream, that would silently drop the rewrite and break allowHashLineComments.
13+
* adjustments it carries the in-buffer '#' and blank line rewrites in BeginToken(), which
14+
* Feature.allowHashLineComments and Feature.allowBlankLinesAsStatementSeparator depend on. Never
15+
* overwrite it with a freshly generated SimpleCharStream, that would silently drop the rewrites and
16+
* break both features.
1617
*/
1718
package net.sf.jsqlparser.parser;
1819

@@ -208,6 +209,14 @@ public final char BeginToken() throws java.io.IOException {
208209
&& AbstractJSqlParser.Dialect.MYSQL.name()
209210
.equals(featureConfiguration.getValue(Feature.dialect))) {
210211
c = mySqlCommentBoundary(c);
212+
} else if (c == '\n' && featureConfiguration != null
213+
&& !featureConfiguration
214+
.getAsBoolean(Feature.allowBlankLinesAsStatementSeparator)
215+
&& peekCharacters(2).equals("\n\n")) {
216+
// Blanking the first newline of the run stops ST_SEMICOLON matching "\n\n\n";
217+
// line and column tracking was already updated when the newline was read.
218+
buffer[bufpos] = ' ';
219+
c = ' ';
211220
}
212221
return c;
213222
}

‎src/main/java/net/sf/jsqlparser/parser/feature/Feature.java‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -897,6 +897,13 @@ public enum Feature {
897897
*/
898898
allowHashLineComments(false),
899899

900+
/**
901+
* treats three consecutive newlines as a statement separator (enabled by default). When
902+
* disabled, blank lines are plain whitespace, so a statement containing them is parsed whole
903+
* instead of ending, or failing to parse, at the first run of blank lines
904+
*/
905+
allowBlankLinesAsStatementSeparator(true),
906+
900907
/**
901908
* allows digit separators in numeric literals, e.g. {@code 1_000_000} (DuckDB, PostgreSQL 16);
902909
* disabled by default, where MySQL reads {@code 2024_01} as an identifier

‎src/site/sphinx/usage.rst‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,7 @@ For several statements at once, use ``CCJSqlParserUtil.parseStatements()``, whic
204204
.. note::
205205

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

208209
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.
209210

@@ -1623,6 +1624,8 @@ The individual features
16231624
- ``".."`` reads as a string literal instead of a quoted identifier (BigQuery, Spark/Databricks, MySQL default ``sql_mode``); combined with ``withBackslashEscapeCharacter``, ``\\"`` does not end the literal, as in MySQL ``"it\\"s"``
16241625
* - ``withHashLineComments``
16251626
- ``#`` starts a line comment
1627+
* - ``withBlankLinesAsSeparator``
1628+
- ``false`` stops two empty lines (``\n\n\n``) ending a statement, so they read as plain whitespace
16261629
* - ``withAdjacentStringLiterals``
16271630
- adjacent string literals concatenate: ``NEWLINE`` (SQL standard, PostgreSQL) or ``WHITESPACE`` (GoogleSQL, Spark/Databricks); ``true`` selects ``NEWLINE``, ``false`` switches it off
16281631
* - ``withAllowComplexParsing``

‎src/test/java/net/sf/jsqlparser/parser/CCJSqlParserUtilTest.java‎

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
import java.util.concurrent.TimeUnit;
4242
import java.util.concurrent.TimeoutException;
4343
import java.util.concurrent.atomic.AtomicBoolean;
44+
import java.util.function.Consumer;
4445

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

582+
@Test
583+
void testBlankLinesAsSeparatorDisabled() throws JSQLParserException {
584+
Consumer<CCJSqlParser> blankLinesAsWhitespace = p -> p.withBlankLinesAsSeparator(false);
585+
586+
assertEquals("SELECT a FROM dual WHERE x = ? AND y = ?",
587+
CCJSqlParserUtil.parse("SELECT a FROM dual WHERE x = ?\n\n\n AND y = ?",
588+
blankLinesAsWhitespace).toString());
589+
assertEquals("SELECT a, b FROM t",
590+
CCJSqlParserUtil.parse("SELECT a,\n\n\n\nb FROM t", blankLinesAsWhitespace)
591+
.toString());
592+
assertEquals("SELECT a FROM t WHERE a > 1",
593+
CCJSqlParserUtil.parse("SELECT a FROM t\n\n\nWHERE a > 1", blankLinesAsWhitespace)
594+
.toString());
595+
assertEquals("SELECT a FROM t UNION SELECT a FROM u",
596+
CCJSqlParserUtil.parse("SELECT a FROM t\n\n\n\n\nUNION\n\n\nSELECT a FROM u",
597+
blankLinesAsWhitespace).toString());
598+
assertEquals("SELECT a FROM t JOIN u ON t.id = u.id",
599+
CCJSqlParserUtil.parse("SELECT a FROM t\n\n\nJOIN u ON t.id = u.id",
600+
blankLinesAsWhitespace).toString());
601+
assertEquals("SELECT a FROM dual",
602+
CCJSqlParserUtil.parse("SELECT a FROM dual\n\n\n", blankLinesAsWhitespace)
603+
.toString());
604+
assertEquals("SELECT 'a\n\n\nb' FROM dual",
605+
CCJSqlParserUtil.parse("SELECT 'a\n\n\nb' FROM dual", blankLinesAsWhitespace)
606+
.toString());
607+
608+
assertEquals(1, CCJSqlParserUtil.parseStatements(
609+
"SELECT a FROM t\n\n\nWHERE a > 1", blankLinesAsWhitespace).size());
610+
assertEquals(3, CCJSqlParserUtil.parseStatements(
611+
"SELECT 1 FROM dual;\n\n\nSELECT 2 FROM dual\ngo\nSELECT 3 FROM dual\n/\n",
612+
blankLinesAsWhitespace).size());
613+
614+
JSQLParserException exception = assertThrows(JSQLParserException.class,
615+
() -> CCJSqlParserUtil.parse("SELECT a\n\n\n\nFROM", blankLinesAsWhitespace));
616+
assertTrue(exception.getCause().getMessage().contains("at line 5, column 1"),
617+
exception.getCause().getMessage());
618+
}
619+
581620
@Test
582621
public void testHashLineCommentsFeature() throws Exception {
583622
// with the flag: MySQL line comments, `#` to end of line,

0 commit comments

Comments
 (0)