Skip to content

parseStatements returns null instead of throwing JSQLParserException when withAllowComplexParsing(false) and the input does not parse #2576

Closed

Description

@stbischof

CCJSqlParserUtil.parseStatements(sql, consumer) returns null instead of throwing JSQLParserException when complex parsing is switched off and the input does not parse. A caller that relies on the declared exception dereferences the result and fails with a NullPointerException of its own; a guard that treats "no exception" as "parsed" is misled.

Version: com.manticore-projects.jsqlformatter:jsqlparser:5.4.351-SNAPSHOT (build 5.4.351-20260910.100601-1), master at b2115ac8.

Reproduce

// default configuration: the documented exception
CCJSqlParserUtil.parseStatements("PRAGMA foo");
// -> JSQLParserException: Encountered: <S_IDENTIFIER> / "PRAGMA", at line 1, column 1 ...

// complex parsing off: null, no exception
Statements s = CCJSqlParserUtil.parseStatements("PRAGMA foo", p -> p.withAllowComplexParsing(false));
// s == null

// same for a broken script and for an empty statement between separators
CCJSqlParserUtil.parseStatements("SELECT * FROM foo; PRAGMA x", p -> p.withAllowComplexParsing(false)); // null
CCJSqlParserUtil.parseStatements("SELECT * FROM foo;;",         p -> p.withAllowComplexParsing(false)); // null

// the single-statement entry point throws as expected with the same option
CCJSqlParserUtil.parse("PRAGMA foo", p -> p.withAllowComplexParsing(false));
// -> JSQLParserException

Also observed: parseStatements("/* nothing */") (default configuration) returns an empty Statements while parse("/* nothing */") throws JSQLParserException; the two entry points disagree on comment-only input.

Where

CCJSqlParserUtil.parseStatements(String, ExecutorService, Consumer) (CCJSqlParserUtil.java:471-501): the simple-parse attempt fails, the complex re-parse is skipped because allowComplexParsing is false, and the method falls through returning the unassigned result instead of rethrowing the first ParseException.

Suggestion

Rethrow the first parse failure as JSQLParserException when the complex re-parse is disabled (the way parse(String, ExecutorService, Consumer) does), and align the two entry points on comment-only input, either both empty or both throwing.

Activity

  1. minleejae commented on Sep 11, 2026

    @minleejae
    Contributor

    @manticore-projects The failure-propagation bug is fixed by #2568. Retested on master eddb1fb5: with withAllowComplexParsing(false), all three reported failing inputs (PRAGMA foo, SELECT * FROM foo; PRAGMA x, and SELECT * FROM foo;;) throw JSQLParserException. The existing ParseStatementsFailureTest tests pass as well.

    The separate comment-only observation still holds: parse("/* nothing */") throws, while parseStatements("/* nothing */") returns an empty statement list. That API-consistency question is separate from the swallowed parse failures; #2568 preserved existing empty-input behavior.

    Could you close the failure-propagation bug as fixed by #2568, and track the comment-only API policy separately if you want the two entry points aligned?

  2. manticore-projects commented on Sep 11, 2026

    @manticore-projects
    Contributor

    Thank you much @minleejae!

    The separate comment-only observation still holds: parse("/* nothing /") throws, while parseStatements("/ nothing */") returns an empty statement list. That API-consistency question is separate from the swallowed parse failures; #2568 preserved existing empty-input behavior.

    We had this discussion before: parseStatements("/* nothing */") clearly is an empty list, because it is not wrong -- just nothing to parse. While parse(...) clearly expects a statement and thus should not return null. (It could return null but really, then you had to check every statement for null, which does not make sense to me).

    @stbischof will verify and close, he is a good friend of this project.

  3. stbischof commented on Sep 11, 2026

    @stbischof
    Author

    Thanks both — and thanks for the quick turnaround on the other three.

    Verified on master eddb1fb5 against the published snapshot 5.4.366-20260911.045237-1.

    What works now

    The failure propagation from #2568 is good. With withAllowComplexParsing(false):

    input before now
    PRAGMA foo returned null throws JSQLParserException
    SELECT * FROM foo; PRAGMA x returned null throws JSQLParserException
    SELECT * FROM foo;; returned null throws JSQLParserException
    SELECT * FROM foo; DROP TABLE foo 2 statements 2 statements, unchanged

    The other three reports are confirmed fixed as well, so those can stay closed: #2573 (SELECT DISTINCT a FROM t classifies instead of throwing), #2574 (a data-modifying CTE is reported as delete not allowed rather than throwing ClassCastException), #2575 (pg_sleep(1) OVER () reports [pg_sleep] and mayModifyData(), CONVERT(x USING y) reports [convert], and sum(id) OVER () with sum proven pure stays clean).

    On the comment-only question

    Agreed, and I withdraw that half of the report:

    parseStatements("/* nothing */") clearly is an empty list, because it is not wrong -- just nothing to parse.

    That is the right call.

    What does not work yet

    The second half of your own rule still has one case:

    While parse(...) clearly expects a statement and thus should not return null.

    CCJSqlParserUtil.parse("");        // returns null
    CCJSqlParserUtil.parse("   ");     // throws JSQLParserException

    An empty string is short-circuited before the parser ever runs, a blank string is not, so the two disagree:

    // CCJSqlParserUtil.java:84
    public static Statement parse(String sql, Consumer<CCJSqlParser> consumer) throws JSQLParserException {
        if (sql == null || sql.isEmpty()) {
            return null;
        }

    The same guard sits on parse(String, ExecutorService, Consumer) (:103), newParser (:138) and parseAST (:154), and the same pattern on parseExpression (:186, :195), parseColumnDataType (:227), parseStatements' expression cousins (:261) and parseCondExpression (:313, :329).

    Remaining task for this issue

    Make parse("") and parse(null) throw JSQLParserException like every other input that yields no statement, so the method never returns null and agrees with parse(" "). parseStatements("") answering an empty list would be consistent with the comment-only behaviour you just confirmed.

    Leaving this open for that; happy to close it once the empty-input case is aligned.

  4. added a commit that references this issue on Sep 11, 2026
    1bef513
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions