使用ANTLR 4解析视图,存储过程和函数中的TSQL联接

寻找对使用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);
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}

是否有更好的方法可以使用已生成的所有其他侦听器方法,而无需手动下降到子节点?我缺少一些魔术,这些魔术在行走时在节点之间保持上下文?

sguidong 回答:使用ANTLR 4解析视图,存储过程和函数中的TSQL联接

您可以直接听这些子规则的enter / exit调用,而不必手动在规则中向下钻取子元素。例如:您收听table_sources,然后从那里下降到table_source_item_joinedtable_name_with_hint。相反,您可以简单地覆盖EnterTable_name_with_hint方法。

编写一个只处理语言非常具体部分的侦听器是完全可以的。看一下this (C++) code in MySQL Workbench,我在其中创建了多个侦听器,每个侦听器仅处理较大构造的子部分或单个对象。有用于创建表,更改表,列定义,触发器等的侦听器。

本文链接:https://www.f2er.com/3156174.html

大家都在问