前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >基于Calcite自定义SQL解析器

基于Calcite自定义SQL解析器

作者头像
麒思妙想
发布2020-07-10 10:15:16
3K0
发布2020-07-10 10:15:16
举报
文章被收录于专栏:麒思妙想麒思妙想

这本应该是《我也能写数据库》系列文章中的一篇,但是最近一直在反思这个系列标题是不是有点不亲民,所以,暂时放弃这个系列标题了。

本文会介绍如何扩展Calcite的SQL解析器使之更符合你的业务需求,或是特殊的语法需求,以前的文章里我们介绍过如何撰写UDF,其实这些都是对SQL进行扩展,只是我们今天会对SQL的结构进行扩展。用一句简单的话说,就是如何定义属于你自己的SQL语法。

Calcite 使用 javacc作为语法解析器,并且使用freemarker作为模板引擎,在编译的时候,freemarker会将配置文件与模板语法文件以及附加文件整体生成最终的语法文件,并通过javacc编译,形成calcite的语法文件。其整个过程如下图所示

下面,我们将从一个简单案例入手,

代码语言:javascript
复制
select ids, name from test where id < 5

是一条正常的SQL,我们要加入关键字 jacky job ,形成一个新的sql语法

代码语言:javascript
复制
jacky job  'select ids, name from test where id < 5'

并且,使之可以正常解析。

构建maven工程

这里注意,需要将编译插件配置好,主要包括freemarker和javacc,否则会出现文件找不到,或是类找不到等奇怪问题,下面是我的pom文件片段

代码语言:javascript
复制
<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.2</version>
            <configuration>
                <source>1.8</source>
                <target>1.8</target>
            </configuration>
        </plugin>
        <plugin>
            <groupId>org.codehaus.mojo</groupId>
            <artifactId>javacc-maven-plugin</artifactId>
            <executions>
                <execution>
                    <id>javacc</id>
                    <goals>
                        <goal>javacc</goal>
                    </goals>
                    <configuration>
                        <sourceDirectory>${project.build.directory}/generated-sources/fmpp</sourceDirectory>
                        <includes>
                            <include>**/Parser.jj</include>
                        </includes>
                        <lookAhead>2</lookAhead>
                        <isStatic>false</isStatic>
                    </configuration>
                </execution>
                <execution>
                    <id>javacc-test</id>
                    <phase>generate-test-sources</phase>
                    <goals>
                        <goal>javacc</goal>
                    </goals>
                    <configuration>
                        <sourceDirectory>${project.build.directory}/generated-test-sources/fmpp</sourceDirectory>
                        <outputDirectory>${project.build.directory}/generated-test-sources/javacc</outputDirectory>
                        <includes>
                            <include>**/Parser.jj</include>
                        </includes>
                        <lookAhead>2</lookAhead>
                        <isStatic>false</isStatic>
                    </configuration>
                </execution>
            </executions>
        </plugin>
        <plugin>
            <groupId>org.apache.drill.tools</groupId>
            <artifactId>drill-fmpp-maven-plugin</artifactId>
            <executions>
                <execution>
                    <configuration>
                        <config>src/main/codegen/config.fmpp</config>
                        <output>${project.build.directory}/generated-sources/fmpp</output>
                        <templates>src/main/codegen/templates</templates>
                    </configuration>
                    <id>generate-fmpp-sources</id>
                    <phase>validate</phase>
                    <goals>
                        <goal>generate</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

复制模板文件

从calcite源码包中,将code\src\main\codegen下所有文件复制到自己的代码路径下

写解析类

创建SqlJacky类,包路径为 org.apache.calcite.sql 因为,SqlJacky需要继承SqlNode类,而该类没有public构造函数。

代码语言:javascript
复制
package org.apache.calcite.sql;
import org.apache.calcite.sql.parser.SqlParserPos;
import org.apache.calcite.sql.util.SqlVisitor;
import org.apache.calcite.sql.validate.SqlValidator;
import org.apache.calcite.sql.validate.SqlValidatorScope;
import org.apache.calcite.util.Litmus;
public class SqlJacky extends SqlNode {
    private String jackyString;
    private SqlParserPos pos;
    public  SqlJacky(SqlParserPos pos, String jackyString){
        super(pos);
        this.pos = pos;
        this.jackyString = jackyString;
    }

    public String getJackyString(){
        System.out.println("getJackyString");
        return this.jackyString;
    }

    @Override
    public SqlNode clone(SqlParserPos sqlParserPos) {
        System.out.println("clone");
        return null;
    }

    @Override
    public void unparse(SqlWriter sqlWriter, int i, int i1) {
        sqlWriter.keyword("jacky");
        sqlWriter.keyword("job");
        sqlWriter.print("\n");
        sqlWriter.keyword("" + jackyString + "");
    }

    @Override
    public void validate(SqlValidator sqlValidator, SqlValidatorScope sqlValidatorScope) {
        System.out.println("validate");
    }

    @Override
    public <R> R accept(SqlVisitor<R> sqlVisitor) {
        System.out.println("accept");
        return null;
    }

    @Override
    public boolean equalsDeep(SqlNode sqlNode, Litmus litmus) {
        System.out.println("equalsDeep");
        return false;
    }
}

在这个解析类里面,其实我们并没有做很多工作,只是在构造器里面,将变量保存起来。

需要注意的是这个方法,unparse ,这里用于解析显示用的,我们将关键字输出出来。

修改config.fmpp文件

找到

代码语言:javascript
复制
package: "org.apache.calcite.sql.parser.impl",

将下方的class,替换成一个你自己的类名,后面会用到。例如

代码语言:javascript
复制
class: "JackySqlParserImpl",

修改Parser.jj文件

首先需要在import的地方引入上面的解析类

代码语言:javascript
复制
import org.apache.calcite.sql.SqlJacky;

然后再后处理代码中加入解析逻辑

代码语言:javascript
复制
SqlNode SqlJacky() :
{
     SqlNode stringNode;
}
{
    <JACKY> <JOB>
    stringNode = StringLiteral()
    {
        return new SqlJacky(getPos(), token.image);
    }
}

接下来找到声明语句的方法

代码语言:javascript
复制
SqlNode SqlStmt() :

代码语言:javascript
复制
|
    stmt = SqlJacky()

加入到适当的位置。

最后在

代码语言:javascript
复制
<DEFAULT, DQID, BTID> TOKEN :

的地方将,jacky 和 job 关键字加入

代码语言:javascript
复制
|   < JACKY: "JACKY">
|   < JOB: "JOB">

由于这个文件比较大,这里就不能贴完整的代码了,下面的连接中,有参考案例。

编译

执行maven的编译命令

测试

在构建测试的时候,注意将自己的解析解析类设置好,即在fmpp里设置的类名

代码语言:javascript
复制
 .setParserFactory(JackySqlParserImpl.FACTORY)

完整测试代码如下

代码语言:javascript
复制
package cn.flinkhub;
import org.apache.calcite.avatica.util.Casing;
import org.apache.calcite.avatica.util.Quoting;
import org.apache.calcite.schema.SchemaPlus;
import org.apache.calcite.sql.SqlNode;
import org.apache.calcite.sql.parser.SqlParser;
import org.apache.calcite.tools.FrameworkConfig;
import org.apache.calcite.tools.Frameworks;
import org.apache.calcite.sql.parser.impl.JackySqlParserImpl;
public class CustomParser {
    public static void main(String[] args) {
        SchemaPlus rootSchema = Frameworks.createRootSchema(true);
        final FrameworkConfig config = Frameworks.newConfigBuilder()
                .parserConfig(SqlParser.configBuilder()
                        //.setLex(Lex.ORACLE)
                        .setParserFactory(JackySqlParserImpl.FACTORY)
                        .setCaseSensitive(false)
                        .setQuoting(Quoting.BACK_TICK)
                        .setQuotedCasing(Casing.TO_UPPER)
                        .setUnquotedCasing(Casing.TO_UPPER)
                        //.setConformance(SqlConformanceEnum.ORACLE_12)
                        .build())
                .build();
//        "jacky 'select ids, name from test where id < 5'";
        String sql = "jacky job  'select ids, name from test where id < 5'";
        SqlParser parser = SqlParser.create(sql, config.getParserConfig());
        try {
            SqlNode sqlNode = parser.parseStmt();
            System.out.println(sqlNode.toString());
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}

执行结果

到这里,解析的部分我们就做完了,后续我计划写一些执行计划相关的文章,让这个语法用起来。

研究calcite的时间有限,有错误的地方欢迎大家勘误。同时也希望对calcite有兴趣的小伙伴和我交流。

鸣谢:这个demo主要参考了 余启大神 的代码,受益匪浅。

参考连接:

https://blog.csdn.net/ccllcaochong1/article/details/93367343

https://github.com/yuqi1129/calcite-test

https://github.com/quxiucheng/apache-calcite-tutorial/tree/a7d63273d0c7585fc65ad250c99a67a201bcb8b5

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-10-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 麒思妙想 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档