Skip to content

CCJSqlParserUtil.parse() silently ignores everything after the first statement's semicolon #2681

Description

@stbischof

CCJSqlParserUtil.parse(sql) returns the first statement and drops whatever follows a
semicolon without any error, so parse("SELECT a FROM t; DROP TABLE t") is a plain Select.
A caller that uses parse() as "the one statement I was given" - a read-only guard, a query
rewriter, a dispatcher - validates or rewrites only the SELECT and never sees the DROP.

Version: com.manticore-projects.jsqlformatter:jsqlparser:5.4.15 (release), grammar
unchanged on master at 60c8dc29. Same on 5.3.167.

Reproduce

CCJSqlParserUtil.parse("SELECT foo.id FROM foo; DROP TABLE foo");
// -> PlainSelect "SELECT foo.id FROM foo", no exception

CCJSqlParserUtil.parse("SELECT foo.id FROM foo; garbage here");
// -> PlainSelect, no exception

CCJSqlParserUtil.parse("SELECT foo.id FROM foo;;");
// -> PlainSelect, no exception
input parse parse(withAllowComplexParsing(false)) parseStatements
SELECT foo.id FROM foo; DROP TABLE foo Select, DROP dropped same 2 statements
SELECT foo.id FROM foo;DROP TABLE foo Select, DROP dropped same 2 statements
SELECT foo.id FROM foo; DROP TABLE foo; Select, DROP dropped same 2 statements
SELECT foo.id FROM foo; garbage here Select, rest dropped same JSQLParserException
SELECT foo.id FROM foo;; Select, rest dropped same JSQLParserException

So parseStatements is strict about the remainder while parse is not, and parse is not
even consistent with itself: SELECT foo.id FROM foo DROP TABLE foo (no semicolon) is
rejected, the same input with a semicolon is accepted and truncated.

Cause

The entry rule Statement() in JSqlParserCC.jjt ends a statement with
( <ST_SEMICOLON> | <EOF> ): after a semicolon nothing requires <EOF>, so the parser returns
as soon as the first statement is complete. CCJSqlParserUtil.parseStatement(parser, ..)
calls parser::Statement and returns its result without looking at the next token, unlike
parseExpression / parseCondExpression, which do check
parser.getNextToken().kind != CCJSqlParserTokenManager.EOF and fail on trailing input.

Suggestion

Make the single-statement entry points require that the input is consumed:

  • in Statement(), [ <ST_SEMICOLON> ] <EOF> after SingleStatement() | Block() (the rule is
    only used for single statements; Statements() has its own loop), or
  • in CCJSqlParserUtil.parseStatement(..), the same EOF check that parseExpression does,
    throwing JSQLParserException for trailing input.

Either way parse("SELECT ..; DROP TABLE ..") should throw, and callers who want a script keep
using parseStatements. This is the counterpart of #2576 (which fixed parseStatements
swallowing failures): a single-statement API should not swallow statements.

Activity

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