簡體   English   中英

Java:Antlr4 MySql獲得單個語句

[英]Java: Antlr4 MySql get individual statements

我使用Java和JDBC來運行MySql代碼。 我想執行DDL腳本,但是JDBC一次只能執行一條語句,這使其不適合直接使用整個.sql文件。

我想做的是使用Antlr4解析.sql文件,以便我可以分解每個單獨的語句,然后使用JDBC迭代執行它們。

我已經走了這么遠:

InputStream resourceAsStream = Main.class.getClassLoader()
            .getResourceAsStream("an-arbitrary-ddl.sql");
CharStream codePointCharStream = CharStreams.fromStream(resourceAsStream);
MySqlLexer tokenSource = new MySqlLexer(new CaseChangingCharStream(codePointCharStream, true));
TokenStream tokenStream = new CommonTokenStream(tokenSource);
MySqlParser mySqlParser = new MySqlParser(tokenStream);
// Where do I go from here?

我確定我只是在搜索正確的術語,因為我是Antlr的新手,它是手動解析代碼的。 從這里找不到任何關於從MySqlParser獲取單個sql語句所需做的MySqlParser 接下來我需要做什么?

解析器不是解決此類問題的正確工具。 語句拆分器非常容易手動編寫,如果您自己執行,則更快。 我在MySQL Workbench中的C ++中實現了這種拆分器。 將其移植到Java並不難。 該代碼非常快(在普通計算機上,不到1秒即可完成1 Mio LOC SQL代碼)。 解析器將需要更長的時間。

我敢肯定,這是可以改進的,因為我創建此問題的最簡單方法是創建一個偵聽器,並為構造函數提供Consumer<String>對象。 偵聽器查看單個語句並以遞歸方式構造它們。 可能有一個更優化的解決方案,但是,如果有的話,我不再有時間去嘗試對此進行優化。

/**
 * @author Paul Nelson Baker
 * @see <a href="https://github.com/paul-nelson-baker/">GitHub</a>
 * @see <a href="https://www.linkedin.com/in/paul-n-baker/">LinkedIn</a>
 * @since 2018-09
 */
public class SqlStatementListener extends MySqlParserBaseListener {

    private final Consumer<String> sqlStatementConsumer;

    public SqlStatementListener(Consumer<String> sqlStatementConsumer) {
        this.sqlStatementConsumer = sqlStatementConsumer;
    }

    @Override
    public void enterSqlStatement(MySqlParser.SqlStatementContext ctx) {
        if (ctx.getChildCount() > 0) {
            StringBuilder stringBuilder = new StringBuilder();
            recreateStatementString(ctx.getChild(0), stringBuilder);
            stringBuilder.setCharAt(stringBuilder.length() - 1, ';');
            String recreatedSqlStatement = stringBuilder.toString();
            sqlStatementConsumer.accept(recreatedSqlStatement);
        }
        super.enterSqlStatement(ctx);
    }

    private void recreateStatementString(ParseTree currentNode, StringBuilder stringBuilder) {
        if (currentNode instanceof TerminalNode) {
            stringBuilder.append(currentNode.getText());
            stringBuilder.append(' ');
        }
        for (int i = 0; i < currentNode.getChildCount(); i++) {
            recreateStatementString(currentNode.getChild(i), stringBuilder);
        }
    }
}

接下來,您需要遍歷這些語句,之前的字符串消費者可以讓您懶惰地將輸出重定向到任何需要的地方。 這就像打印到stdout一樣簡單,但是,可以很容易地將其附加到列表中。

public List<String> mySqlStatementsFrom(String sourceCode) {
    List<String> statements = new ArrayList<>();
    mySqlStatementsToConsumer(sourceCode, statements::add);
    return statements;
}

public void mySqlStatementsToConsumer(String sourceCode, Consumer<String> mySqlStatementConsumer) {
    CharStream codePointCharStream = CharStreams.fromString(sourceCode);
    MySqlLexer tokenSource = new MySqlLexer(new CaseChangingCharStream(codePointCharStream, true));
    TokenStream tokenStream = new CommonTokenStream(tokenSource);
    MySqlParser mySqlParser = new MySqlParser(tokenStream);

    SqlStatementListener statementListener = new SqlStatementListener(mySqlStatementConsumer);
    ParseTreeWalker.DEFAULT.walk(statementListener, mySqlParser.sqlStatements());
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM