Java: Antlr4 MySql получить отдельные инструкции

Я использую 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. Что мне делать дальше?

Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
1
0
157
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

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

Я ценю ваш ответ, но на самом деле он мне не помогает. Я использовал подобные простые решения и раньше, однако я хочу лучше использовать Antlr, поэтому я старался изо всех сил его использовать. К большому сожалению, вероятно, есть люди, которым приходится использовать Antlr для своих проектов, и они могут наткнуться на этот ответ при поиске ресурсов, которые им тоже не помогут.

Paul Nelson Baker 27.09.2018 19:10
Ответ принят как подходящий

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

Вы профилировали свое решение? Сколько времени требуется, например, для создания файла в 1 миллион строк?

Mike Lischke 27.09.2018 21:13

У вас есть файл размером 1 миллион, который я могу использовать? Я сделаю это, если у вас есть такая возможность, но, как я уже сказал, я хочу стать лучше в Antlr. Как только я пойму, как правильно использовать Antlr, я смогу оптимизировать. В противном случае такая оптимизация преждевременна. Хотя я могу оценить ваше решение, которое очень быстро разбивает сценарии на отдельные операторы, меня больше беспокоит общее решение, которое я могу расширить (например, несколько диалектов SQL или даже что-то, не связанное с SQL), а не одноразовое решение. это применимо только к этому индивидуальному случаю и не учит меня больше о языковой обработке / синтаксическом анализе.

Paul Nelson Baker 27.09.2018 22:12

Другие вопросы по теме