基于 antlr4 实现 sql 解析

514 阅读1分钟

前置准备

SQL 解析语法模版

idea 安装antlr4 插件

需要在IDEA中安装antlr4插件

安装antlr4.jpeg

新建Maven工程

新建Maven工程,导入maven 依赖

 <dependencies> 
     <dependency>
            <groupId>org.antlr</groupId>
            <artifactId>antlr4-runtime</artifactId>
            <version>4.8-1</version>
        </dependency>
 </dependencies>  
  
 <plugins> 
         <plugin>
                <groupId>org.antlr</groupId>
                <artifactId>antlr4-maven-plugin</artifactId>
                <version>4.8-1</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>antlr4</goal>
                        </goals>
                        <phase>none</phase>
                    </execution>
                </executions>
                <configuration>
                    <outputDirectory>src/main/java</outputDirectory>
                    <!--<listener>true</listener>-->
                    <visitor>true</visitor>
                    <!--<treatWarningsAsErrors>true</treatWarningsAsErrors>-->
                </configuration>
          </plugin>
</plugins>

idea 配置修改

问题原因:

这是因为生成的源文件有数万行,源文件过大导致 idea 拒绝编译过大的源文件。

解决方案:

如果 protoc 生成的 class 文件找不到,说明 protoc 文件没有没有正确编译。 可以调整 IDEA 的默认参数, 然后重启 IntelliJ IDEA 使得 IDE 能够正确找到 protoc 生成的 Java 类。

step1: 打开 Help -> Edit Custom Properties 菜单
step2: 增加一行: idea.max.intellisense.filesize=50000 
step3: 重启 IDE

实现操作

将sql 语法解析模版放入 resource 文件夹

image.png

编译语法文件生成java语法解析类

image.png

生成文件

image.png

案例

获取一个SQL语句中的表名

public class GetTableNamesListener extends MySqlParserBaseListener {
    private final Set<String> tableNameSet = new HashSet<String>();

    @Override
    public void enterTableSources(MySqlParser.TableSourcesContext ctx) {
        List<MySqlParser.TableSourceContext> tableSourceContexts = ctx.getRuleContexts(MySqlParser.TableSourceContext.class);
        for (MySqlParser.TableSourceContext tableSource : tableSourceContexts) {
            //通过tableSourceItems获取表名
            getTableNameByTableSourceItems(tableSource.getRuleContexts(MySqlParser.TableSourceItemContext.class));
            //获取join部分
            List<MySqlParser.OuterJoinContext> joinContexts = tableSource.getRuleContexts(MySqlParser.OuterJoinContext.class);
            for (MySqlParser.OuterJoinContext joinContext : joinContexts) {
                List<MySqlParser.TableSourceItemContext> tableSourceItemContexts = joinContext.getRuleContexts(MySqlParser.TableSourceItemContext.class);
                getTableNameByTableSourceItems(tableSourceItemContexts);
            }
        }
    }

    private void getTableNameByTableSourceItems(List<MySqlParser.TableSourceItemContext> tableSourceItems) {
        for (MySqlParser.TableSourceItemContext tableSourceItem : tableSourceItems) {
            List<MySqlParser.TableNameContext> tableNameContexts = tableSourceItem.getRuleContexts(MySqlParser.TableNameContext.class);
            for (MySqlParser.TableNameContext tableNameContext : tableNameContexts) {
                tableNameSet.add(tableNameContext.getText());
            }
        }
    }

    public Set<String> getTableNameSet() {
        return tableNameSet;
    }
}

执行代码:

    public static void main(String[] args) {
        String sql = "SELECT t1.column1,t1.column2,t1.column3,t2.xy from tableC t1 left join tableA t2 on t1.id=t2.oid where t1.column1 = 1 and t2.yy=6";
        System.out.println(sql);
        MySqlLexer lexer = new MySqlLexer(CharStreams.fromString(sql.toUpperCase()));
        MySqlParser parser = new MySqlParser(new CommonTokenStream(lexer));
        //定义GetTableNamesListener
        GetTableNamesListener listener = new GetTableNamesListener();
        ParseTreeWalker.DEFAULT.walk(listener, parser.sqlStatements());
        Set<String> tableNameSet = listener.getTableNameSet();
        for (String tableName : tableNameSet) {
            System.out.println(tableName);
        }
    }

返回结果:

SELECT t1.column1,t1.column2,t1.column3,t2.xy from tableC t1 left join tableA t2 on t1.id=t2.oid where t1.column1 = 1 and t2.yy=6
TABLEA
TABLEC