【问题标题】:Antlr parser SQL how to distinguish between a field and a table?Antlr解析器SQL如何区分字段和表?
【发布时间】:2020-06-22 12:42:17
【问题描述】:

我写了一些Antlr4规则来解析SQL,只是想区分字段和表。但是他们做了一些意想不到的事情。我的规则:

grammar Col;
stat : SELECT select_list FROM table_ref_list;
select_list : select_ele (',' select_ele)* ;
select_ele : Subquery_in_field_nor    //subquery select column
            | ID '(' .*? ')'  //function calls
            | NoDotId '(' .*? ')' NoDotId '(' .*? ')'  //window function call
            | ID             //like column of tab
            | DIGIT          //number
            | STRING         //like this 'dad'
            ;


table_ref_list
    : table_ref (',' table_ref)*
    ;

table_ref:table_block (NoDotId)?;

table_block : ID                   //So much like select_ele
          | Subquery_in_field_nor
          ;

Subquery_in_field_nor : '(' (Subquery_in_field | ~[()])* ')';  //Resolving function nesting
Subquery_in_field : '(' .*? ')' ;
SELECT : [Ss][Ee][Ll][Ee][Cc][Tt];
FROM :[Ff][Rr][Oo][Mm];
NL : [ \r\n]+ ->skip;
ID : [A-Za-z] [A-Za-z0-9.]*;
NoDotId : [A-Za-z] [A-Za-z0-9]*;  
DIGIT : ('-')? [0-9]+('.' [0-9]+)?;
STRING : '\'' .*? '\'';

我的sql文件是这样的

SELECT substr(A.EMPNO,1,2),
       A.ENAME,
       '1',
       'wwet',
       18,
       A.DEPTNO FROM EMP A

显示消息

line 1:13 missing FROM at '(A.EMPNO,1,2)'
line 3:7 mismatched input ''1'' expecting {Subquery_in_field_nor, ID}
line 4:7 mismatched input ''wwet'' expecting {Subquery_in_field_nor, ID}
line 5:7 mismatched input '18' expecting {Subquery_in_field_nor, ID}
(stat SELECT (select_list (select_ele substr)) <missing FROM> (table_ref_list (table_ref (table_block (A.EMPNO,1,2))) , (table_ref (table_block A.ENAME)) , (table_ref (table_block '1')) , (table_ref (table_block 'wwet')) , (table_ref (table_block 18)) , (table_ref (table_block A.DEPTNO))))

我不知道为什么?为什么我用户select_list : select_ele (',' select_ele)*,但仍然匹配table_ref?

【问题讨论】:

    标签: sql parsing antlr antlr4


    【解决方案1】:

    substr(A.EMPNO,1,2)select_ele 不匹配,因为它会生成令牌:

    • ID: substr
    • Subquery_in_field_nor: (A.EMPNO,1,2)

    而不是通过两种选择中的任何一种:

    • | ID '(' .*? ')' //function calls
    • | NoDotId '(' .*? ')' NoDotId '(' .*? ')' //window function call

    '(' .*? ')' 部分被解释为:匹配一个( 标记,后跟零个或多个其他标记,以) 标记结尾。但是因为 ANTLR 的词法分析器在对输入进行标记时会尝试获取尽可能多的字符,所以 (A.EMPNO,1,2) 将始终与单个 Subquery_in_field_nor 标记匹配。

    除了词法分析器中的贪婪标记之外,您还必须注意,当 2 个或更多词法分析器规则可以匹配相同的字符时,第一个定义的“获胜”。所以输入select可以匹配SELECTIDNoDotId。但是由于首先定义了SELECT,因此该输入将始终成为SELECT 标记。但是,输入foo 可以与IDNoDotId 匹配,并且由于首先定义了ID,因此它将获胜。您会注意到根本不会有 NoDotId 令牌,因为它匹配的任何内容也与 ID 匹配。

    这是一个稍加修改的语法,适用于您的输入:

    grammar Col;
    
    stat
      : SELECT select_list FROM table_ref_list
      ;
    
    select_list
     : select_ele (',' select_ele)*
     ;
    
    select_ele
     : id Subquery_in_field_nor (id Subquery_in_field_nor)?
     | id
     | DIGIT
     | STRING
     ;
    
    table_ref_list
     : table_ref (',' table_ref)*
     ;
    
    table_ref
     : table_block id?
     ;
    
    table_block
     : id
     | Subquery_in_field_nor
     ;
    
    id
     : ID ('.' ID)*
     ;
    
    Subquery_in_field_nor : '(' (Subquery_in_field | ~[()])* ')';  //Resolving function nesting
    Subquery_in_field     : '(' .*? ')' ;
    SELECT                : [Ss][Ee][Ll][Ee][Cc][Tt];
    FROM                  : [Ff][Rr][Oo][Mm];
    NL                    : [ \r\n]+ ->skip;
    ID                    : [A-Za-z] [A-Za-z0-9]*;
    DIGIT                 : ('-')? [0-9]+('.' [0-9]+)?;
    STRING                : '\'' .*? '\'';
    

    或者更好的是,从 ANTLR4 Github 存储库中获取现有的 SQL 语法:https://github.com/antlr/grammars-v4 请注意,所有这些语法都是来自社区的开源贡献:正确测试它们,因为它们中的许多都会有限制(包括准确性和在性能方面)。

    【讨论】:

    • 哦,兄弟。非常感谢。我工作有点忙,一直没有机会回复你。谢谢你的帮助。我没有不使用GITHUB主要是因为解析字段的时候只支持native函数,而且我是新手,想等一万多行代码
    • 你好,兄弟。我是来寻求帮助的。我整天都在努力。我一直在试图弄清楚使用什么 antlr4 语法来区分插入后的 () 和任何函数调用
    猜你喜欢
    • 2011-01-30
    • 1970-01-01
    • 1970-01-01
    • 2011-10-18
    • 1970-01-01
    • 1970-01-01
    • 2012-04-25
    • 2016-09-15
    • 1970-01-01
    相关资源
    最近更新 更多