Java: Antlr4 MySql получить отдельные заявления - PullRequest
0 голосов
/ 15 сентября 2018

Я использую 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 и разбираю код вручную.Отсюда я не могу найти никаких ссылок на то, что мне нужно сделать, чтобы получить отдельные операторы SQL из MySqlParser.Что мне нужно делать дальше?

Ответы [ 2 ]

0 голосов
/ 27 сентября 2018

Я уверен, что это можно улучшить, однако, как самый простой способ создать это, я создал слушателя и предоставил конструктору объект 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);
        }
    }
}

Далее вам нужно пройти через операторы, получатель строки из предыдущих версий позволяет вам ленивоперенаправить вывод туда, куда вам нужно.Это может быть так же просто, как просто печатать на стандартный вывод, однако его так же легко использовать для добавления в список.

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());
}
0 голосов
/ 15 сентября 2018

Парсер не является подходящим инструментом для решения подобных проблем.Разделитель операторов довольно легко написать вручную и намного быстрее, если вы делаете это самостоятельно.Я реализовал такой сплиттер в C ++ в MySQL Workbench .Не должно быть сложно перенести это на Java.Код очень быстрый (1 код SQL Mio LOC менее чем за 1 секунду на средней машине).Парсеру понадобится намного дольше.

...