Использование ANTLR 4 для анализа соединений TSQL в представлениях, хранимых процедурах и функциях - PullRequest
0 голосов
/ 06 ноября 2019

Требуется проверка правильности подхода к использованию ANTLR 4 для анализа соединений SQL.

Я создал лексер, парсер и посетитель из следующей грамматики.

https://github.com/antlr/grammars-v4/tree/master/tsql

Затем я могу создать дерево разбора (в этом примере для представления), и я могу запустить обход дерева, используя реализованный мной слушатель.

ICharStream stream = CharStreams.fromstring(view);
ITokenSource lexer = new TSqlLexer(stream);
ITokenStream tokens = new CommonTokenStream(lexer);
TSqlParser parser = new TSqlParser(tokens);
parser.BuildParseTree = true;
IParseTree tree = parser.create_view();

TSqlListener listener = new TSqlListener();
ParseTreeWalker.Default.Walk(listener, tree);

Мой вопрос. Является ли мой метод извлечения токенов для объединений «правильным» и наиболее эффективным способом сделать это.

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

public void EnterTable_sources([NotNull] TSqlParser.Table_sourcesContext context)
{
    var table_sources = context.table_source().ToList();

    foreach (var table_source in table_sources)
    {
        var item = table_source.table_source_item_joined();

        if (item != null)
        {
            //first aliases
            var source_item = item.table_source_item();

            if (source_item != null)
            {
                TableAlias tableAlias = new TableAlias();

                var table_name = source_item.table_name_with_hint();

                if (table_name != null)
                {
                    var fullTableName = table_name.GetText();

                    if (fullTableName.Contains('.'))
                    {
                        var nameParts = fullTableName.Split('.').ToList();

                        for (int i = 0; i <nameParts.Count; i++)
                        {
                            tableAlias.AddParts(nameParts);
                        }
                    }
                    else
                    {
                        tableAlias.AddParts(fullTableName);
                    }
                }

                var table_alias = source_item.as_table_alias();

                if (table_alias != null)
                {
                    tableAlias.Alias = table_alias.GetText();
                }

                JoinAnalysis.Aliases.Add(tableAlias);
            }

            var join_parts = item.join_part();

            foreach (var join_part in join_parts)
            {
                var table_source_joins = join_part.table_source();

                if (table_source_joins != null)
                {
                    //The join table and alias
                    var table_source_item_joined = table_source_joins.table_source_item_joined();

                    if (table_source_item_joined != null)
                    {
                        var joinAlias = new TableAlias();
                        var table_source_item = table_source_item_joined.table_source_item();
                        var table_name = table_source_item.table_name_with_hint();

                        if (table_name != null)
                        {
                            var fullTableName = table_name.GetText();

                            if (fullTableName.Contains('.'))
                            {
                                var nameParts = fullTableName.Split('.').ToList();
                                joinAlias.AddParts(nameParts);
                            }
                            else
                            {
                                joinAlias.AddParts(fullTableName);
                            }
                        }

                        if (table_source_item != null)
                        {
                            var table_alias = table_source_item.as_table_alias();

                            if (table_alias != null)
                            {
                                joinAlias.Alias = table_alias.GetText();
                            }
                        }

                        if (joinAlias.Alias != null)
                        {
                            JoinAnalysis.Aliases.Add(joinAlias);
                        }
                    }
                }

                var search_condition = join_part.search_condition();

                if (search_condition != null)
                {
                    //The join conditions
                    var conditions = search_condition.search_condition_and();

                    if (conditions != null)
                    {
                        foreach (var condition in conditions)
                        {
                            if (condition != null)
                            {
                                foreach (var search_condition_not in condition.search_condition_not())
                                {
                                    JoinCondition joinCondition = new JoinCondition();
                                    joinCondition.LineNumber = search_condition_not.Start.Line;
                                    var conditionText = search_condition_not.GetText();
                                    joinCondition.JoinConditionText = conditionText;
                                    var splitCondition = conditionText.Split("=");

                                    if (splitCondition.Length == 2)
                                    {
                                        joinCondition.LeftPart = splitCondition[0];
                                        joinCondition.RightPart = splitCondition[1];
                                    }

                                    JoinAnalysis.JoinConditions.Add(joinCondition);
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}

Есть ли лучший способ сделать это, используя все другие методы слушателя, которые были сгенерированы, не спускаясь вручную в дочерние узлы? Есть ли какая-то магия, которой мне не хватает, которая хранит контекст между узлами, когда они ходят?

1 Ответ

0 голосов
/ 06 ноября 2019

Вместо того, чтобы вручную переходить к подэлементам в правиле, вы можете просто прослушивать вызовы входа / выхода для этих подправил. Как пример: вы слушаете table_sources и понижаетесь до table_source_item_joined оттуда до table_name_with_hint. Вместо этого вы можете просто переопределить метод EnterTable_name_with_hint.

Полностью нормально написать слушателя, который обрабатывает только очень специфические части языка. Посмотрите на этот (C ++) код в MySQL Workbench , где я создал несколько слушателей, каждый из которых просто обрабатывал часть более крупного конструкта или отдельные объекты. Есть прослушиватели для создания таблицы, изменения таблицы, определения столбцов, триггеры и многое другое.

...