Apache Calcite解析表名和字段名信息(未进行validate)_不悔将来的博客-程序员宅基地

技术标签: java  OLAP  大数据  

目录

 

前言

解析实现


前言

市面上有很多做SQL解析的语法引擎,如阿里的druid,ANTLR等。楼主之前在南京某宁公司使用ANTLR和阿里的druid做过表/字段级的血缘解析,也一直认为ANTLR/druid做解析做解析很方便,尤其是ANTLR的visitor模式,想要哪个要哪个。

但近期由于业务的复杂,需要将SQL语法不仅解析出来还需要做一定的转义操作。参考了spark源码是如何处理ANTLR的,结果发现太过复杂(其实是没大看懂),因此采用了最简单的replace方式,可是该方式会存在问题(你们懂的!!)

为此,研究了一下据说很牛逼的Calcite。但不得不承认,Calcite真复杂,目前还只是小白!!

 

解析实现

import org.apache.calcite.config.Lex;
import org.apache.calcite.sql.*;
import org.apache.calcite.sql.parser.SqlParser;
import org.apache.calcite.sql.parser.SqlParserPos;
import org.apache.calcite.sql.parser.impl.SqlParserImpl;
import org.apache.calcite.sql.validate.SqlConformanceEnum;
import org.apache.calcite.util.ImmutableBeans;


/**
 * Created with IntelliJ IDEA.
 * Description:
 *
 * @Author: yuanhongjun
 * DateTime: 2021-04-13 9:25
 */
public class CalciteSQLParser {

    private static SqlParser.Config config = SqlParser.configBuilder().setLex(Lex.MYSQL).setCaseSensitive(true).setConformance(SqlConformanceEnum.MYSQL_5).build();
    //.setConformance(SqlConformanceEnum.MYSQL_5)  limit 10,10
    //.setCaseSensitive(true)大小写敏感 

    private static SqlParser.Config DEFAULT = (ImmutableBeans.create(SqlParser.Config.class)).withLex(Lex.MYSQL).withIdentifierMaxLength(128).withConformance(SqlConformanceEnum.DEFAULT).withParserFactory(SqlParserImpl.FACTORY);


    public static void main(String[] args) throws Exception {


//        String sql = "select sum(b) as dd , b.c from db.d where e = x and f not in (x,d)";

//        String sql = "SELECT sum(x.dd) as xx ,2 from db.a x where id = xx and c = 'zz' group by xx order by dd limit 10 " ;
//        String sql = "SELECT sum(x.dd) as xx ,2 from db.a x where id = xx and c = 'zz' order by dd limit 10 ";
//        String sql = "SELECT sum(f) as xx,e FROM db.B left join B.dd on dd.xx=b.cc WHERE g = h";
//        String sql = "SELECT sum(f) as xx,e FROM db.B left join (select xx from B.dd union select xx from d.dddddd) as bdd on dd.xx=b.cc WHERE g = h";
        String sql = "SELECT sum(x.dd) as xx ,2 from db.a x where id = xx and c = 'zz'  " +
                "union all SELECT sum(f) as xx,e FROM db.B left join B.dd on dd.xx=b.cc WHERE g = h limit 10,10";
        //当存在子查询和order by的时候都可能需要传入到Select

        SqlParser sqlParser = SqlParser.create(sql, config);
        try {
            SqlNode sqlNode = sqlParser.parseQuery();

            System.out.println(sqlNode.toString());

            hanlerSQL(sqlNode);

        } catch (Exception e) {
            throw new RuntimeException("", e);
        }

    }

    private static void hanlerSQL(SqlNode sqlNode) {
        SqlKind kind = sqlNode.getKind();
        switch (kind) {
            case SELECT:
                hanlerSelect(sqlNode);
                break;
            case UNION:
                ((SqlBasicCall) sqlNode).getOperandList().forEach(node -> {
                    hanlerSQL(node);
                });

                break;
            case ORDER_BY:
                handlerOrderBy(sqlNode);
                break;
        }
    }

    private static void handlerOrderBy(SqlNode node) {
        SqlOrderBy sqlOrderBy = (SqlOrderBy) node;
        SqlNode query = sqlOrderBy.query;
        hanlerSQL(query);
        SqlNodeList orderList = sqlOrderBy.orderList;
        handlerField(orderList);
    }


    private static void hanlerSelect(SqlNode select) {

        SqlSelect sqlSelect = (SqlSelect) select;

        //TODO 改写SELECT的字段信息

        SqlNodeList selectList = sqlSelect.getSelectList();
        //字段信息
        selectList.getList().forEach(list -> {
            handlerField(list);
        });

        handlerFrom(sqlSelect.getFrom());

        if (sqlSelect.hasWhere()) {
            handlerField(sqlSelect.getWhere());
        }

        if (sqlSelect.hasOrderBy()) {
            handlerField(sqlSelect.getOrderList());
        }

        SqlNodeList group = sqlSelect.getGroup();
        if (group != null) {
            group.forEach(groupField -> {
                handlerField(groupField);
            });
        }


        SqlNode fetch = sqlSelect.getFetch();
        if (fetch != null) {
            //TODO limit
        }

    }

    private static void handlerFrom(SqlNode from) {
        SqlKind kind = from.getKind();

        switch (kind) {
            case IDENTIFIER:
                //最终的表名
                SqlIdentifier sqlIdentifier = (SqlIdentifier) from;
                //TODO 表名的替换,所以在此之前就需要获取到模型的信息
                System.out.println("==tablename===" + sqlIdentifier.toString());
                break;
            case AS:
                SqlBasicCall sqlBasicCall = (SqlBasicCall) from;
                SqlNode selectNode = sqlBasicCall.getOperandList().get(0);
                hanlerSQL(selectNode);
                break;
            case JOIN:
                SqlJoin sqlJoin = (SqlJoin) from;
                SqlNode left = sqlJoin.getLeft();
                hanlerSQL(left);
                SqlNode right = sqlJoin.getRight();
                hanlerSQL(right);
                SqlNode condition = sqlJoin.getCondition();
                handlerField(condition);
                break;
            case SELECT:
                hanlerSQL(from);
                break;
        }
    }


    private static void handlerField(SqlNode field) {
        SqlKind kind = field.getKind();
        switch (kind) {
            case AS:
                SqlNode[] operands_as = ((SqlBasicCall) field).operands;
                SqlNode left_as = operands_as[0];
                handlerField(left_as);
                break;
            case IDENTIFIER:
                //表示当前为子节点
                SqlIdentifier sqlIdentifier = (SqlIdentifier) field;
                System.out.println("===field===" + sqlIdentifier.toString());
                break;
            default:
                if (field instanceof SqlBasicCall) {
                    SqlNode[] nodes = ((SqlBasicCall) field).operands;
                    for (int i = 0; i < nodes.length; i++) {
                        handlerField(nodes[i]);
                    }
                }
                if (field instanceof SqlNodeList) {
                    ((SqlNodeList) field).getList().forEach(node -> {
                        handlerField(node);
                    });
                }
                break;
        }


    }


}

代码只是完成了解析的操作,具体怎么封装没有实现,后面会继续研究Calcite的validate以及如何根据不同存储引擎转换成对应的查询语句,敬请期待……

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/junjun56/article/details/115870189

智能推荐

vmware Esxi 回收Thin模式磁盘空间_开心自由天使的博客-程序员宅基地

第一部分 回收Thin模式磁盘空间背景:在使用Thin模式的磁盘,空间不停的增加,通过通常的方法删除文件,释放可用空间,在虚拟机中查看已使用空间和在vsphere的存储中查看的已使用空间不一样,存储中的空间好像只会增加不会自动减少,根据搜索的资料总结出来释放可用空间的方法如下环境:Vsphere 6.0第一步: 虚拟机中处理如果是windows系统,请使用sdelete工具进行置零,此工具...

联发科mt6735详细参数_够用就好!联发科MT6735参数详解_AXBQ的博客-程序员宅基地

联发科在2014年10月发布了首款集成全网通4G基带的四核处理器-MT6735,后续该处理器衍生MTK6735P/M两个版本,阉割了部分网络性能和降低主频。时间过去快3年了,目前仍有大量新款低端手机采用MT6735,很多消费者开始关注它的性能:够用吗?下面我来详细介绍MT6735参数和性能特点。CPU部分,其采用四核心64位Cortex-A53架构设计,主频1.3-1.5GHz,这和上午我们报道的...

Unsatisfied dependency expressed through field /Injection of autowired dependencies failed 解决方法_MyySophia的博客-程序员宅基地

根据报错的关键字,大底的意思就是依赖方面的问题。以下为启动程序时遇到的错误:org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'deleteRedisData': Unsatisfied dependency expressed through f...

Bluemix平台一步一步搭建ZooKeeper + Mesos + Marathon平台管理Docker集群之一_rapheler的博客-程序员宅基地

最近在Youtube看Docker视频的过程中不幸看到了Mesos的介绍,然后就有一种一见如故的感觉,最终根据mesosphere官网的文档在IBM的Bluemix虚拟机上搭建了基于ZooKeeper + Mesos + Marathon的平台。搭建之前先简单了解下各个组件是做什么的。(来自wikipedia及其他网络)ZooKeeper:Zookeeper

Android Camera2 API,Android面试题中高级-程序员宅基地

2.2.3、SENSOR_ORIENTATION2.2.4、FLASH_INFO_AVAILABLE2.2.5、SCALER_AVAILABLE_MAX_DIGITAL_ZOOM2.2.6、LENS_INFO_MINIMUM_FOCUS_DISTANCE2.2.7、INFO_SUPPORTED_HARDWARE_LEVEL2.3、CameraDevice类2.4、CameraCaptureSession类2.5、CameraRequest类0、相关文章:=======Android C

随便推点

UE4 Lerp(蓝图)_ue lerp_程序猿老李CR的博客-程序员宅基地

线性插值或 Lerp 函数会返回一个基于 Alpha 输入的 A 与 B 输入间的混合值。 在给定的示例中,当 Alpha 值为 0 时,将返回 A 的 100% 的值。 当 Alpha 值为 1 时,将返回 B 的 100% 的值。...

获取元素的定位参考元素和定位值(JavaScript 和 jQuery)_元素定位获取第二值的函数_MrShyZhang的博客-程序员宅基地

JavaScript获取元素的定位参考元素和定位值方法1. offsetParent 获取元素的定位参考元素    element.offsetParent 2. offsetLeft 获取元素到定位参考元素的左边距离    element.offsetLeft 3. offsetTop 获取元素的定位参考元素的上边距离    element.offsetTop  &amp;lt;!DOCTYPE htm...

NRF52系列开发环境搭建_m_pfly_fish的博客-程序员宅基地

前言本博客使用keil ide和15.3版本的sdk作为环境进行nrf52832芯片开发官方写的软件开发指导书链接:https://infocenter.nordicsemi.com/index.jsp?topic=%2Fug_getting_started%2FUG%2Fcommon%2Fnordic_tools.html&amp;cp=1_0_1官方的开发者社区:https...

python cv2模块 createimage_Python cv.CreateImage方法代码示例_weixin_39750854的博客-程序员宅基地

本文整理汇总了Python中cv2.cv.CreateImage方法的典型用法代码示例。如果您正苦于以下问题:Python cv.CreateImage方法的具体用法?Python cv.CreateImage怎么用?Python cv.CreateImage使用的例子?那么恭喜您, 这里精选的方法代码示例或许可以为您提供帮助。您也可以进一步了解该方法所在模块cv2.cv的用法示例。在下文中一共展...

Launchpad的背景变的透明不再模糊_weixin_33970449的博客-程序员宅基地

为什么80%的码农都做不了架构师?&gt;&gt;&gt; ...

LeetCode 898. Bitwise ORs of Subarrays 子数组按位或操作_importsys的博客-程序员宅基地

LeetCode 898. Bitwise ORs of Subarrays 子数组按位或操作898. Bitwise ORs of Subarrays题目描述示例:解答代码898. Bitwise ORs of Subarrays题目描述We have an array A of non-negative integers.For every (contiguous) subarray ...

推荐文章

热门文章

相关标签