MySQL: simple-form CREATE FUNCTION (RETURN expr without BEGIN...END) breaks subsequent statements in multi-statement script
Describe the bug
When a MySQL script contains a simple-form CREATE FUNCTION (body is a single RETURN expr; without BEGIN...END) followed by other statements, SQLUtils.parseStatements(sql, "mysql") throws ParserException: syntax error ... token SELECT. BEGIN...END bodies work fine. Affects all tested versions 1.2.14 ~ 1.2.28 (latest).
To Reproduce
List<SQLStatement> stmts = SQLUtils.parseStatements(
"CREATE FUNCTION f() RETURNS INT RETURN 1;\nSELECT 1;", "mysql");
// ParserException: syntax error. pos 36, line 2, column 2, token SELECTCREATE FUNCTION ... RETURN 1; followed by another CREATE FUNCTION passes on 1.2.14 but also fails on 1.2.27/1.2.28 (not supported ... token CREATE), because only case SELECT (and now case CREATE) guards on the semi flag.
Expected behavior
The script parses into 2 statements: SQLCreateFunctionStatement + SQLSelectStatement.
Root cause
SQLStatementParser.parseReturn() consumes the terminating SEMI itself (accept(Token.SEMI) + setAfterSemi). Back in parseStatementList, the local semi flag therefore stays false, and the case SELECT branch's guard i > 0 && dbType != odps && !semi wrongly throws for the next statement. case CREATE has no such guard on 1.2.14, which is why function + function accidentally passes there.
Suggested fix
Let parseReturn() not consume the SEMI; leave statement-terminating semicolons to the SEMI branch of parseStatementList, which sets semi = true and afterSemi uniformly:
public SQLStatement parseReturn() {
if (lexer.token == Token.RETURN || lexer.identifierEquals("RETURN")) {
lexer.nextToken();
}
SQLReturnStatement stmt = new SQLReturnStatement();
if (lexer.token != Token.SEMI) {
stmt.setExpr(this.exprParser.expr());
}
return stmt; // do NOT accept(SEMI) here
}We verified this change locally (via a MySqlStatementParser subclass): function+SELECT, function+function, BEGIN...END+SELECT, and mixed scripts all parse correctly.
Source: alibaba/druid