Skip to content

Commit ab8f394

Browse files
edeadingtonclaude
andcommitted
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>
1 parent 19f12ea commit ab8f394

4 files changed

Lines changed: 59 additions & 0 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
@@ -194,6 +194,11 @@ public P withHashLineComments(boolean allowHashLineComments) {
194194
return withFeature(Feature.allowHashLineComments, allowHashLineComments);
195195
}
196196

197+
public P withBlankLinesAsSeparator(boolean allowBlankLinesAsStatementSeparator) {
198+
return withFeature(Feature.allowBlankLinesAsStatementSeparator,
199+
allowBlankLinesAsStatementSeparator);
200+
}
201+
197202
public P withUnparenthesizedSubSelects() {
198203
return withFeature(Feature.allowUnparenthesizedSubSelects, true);
199204
}

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,14 @@ public final char BeginToken() throws java.io.IOException {
208208
&& AbstractJSqlParser.Dialect.MYSQL.name()
209209
.equals(featureConfiguration.getValue(Feature.dialect))) {
210210
c = mySqlCommentBoundary(c);
211+
} else if (c == '\n' && featureConfiguration != null
212+
&& !featureConfiguration
213+
.getAsBoolean(Feature.allowBlankLinesAsStatementSeparator)
214+
&& peekCharacters(2).equals("\n\n")) {
215+
// Blanking the first newline of the run stops ST_SEMICOLON matching "\n\n\n";
216+
// line and column tracking was already updated when the newline was read.
217+
buffer[bufpos] = ' ';
218+
c = ' ';
211219
}
212220
return c;
213221
}

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -889,6 +889,13 @@ public enum Feature {
889889
*/
890890
allowHashLineComments(false),
891891

892+
/**
893+
* treats three consecutive newlines as a statement separator, like `;`; enabled by default.
894+
* When disabled, blank lines are plain whitespace, so a statement containing them is parsed
895+
* whole instead of being cut off at the first run of blank lines
896+
*/
897+
allowBlankLinesAsStatementSeparator(true),
898+
892899
/**
893900
* allows sub selects without parentheses, e.g. `select * from dual where 1 = select 1`
894901
*/

‎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)