如何实现一个SQL解析器( 五 )


如何实现一个SQL解析器

文章插图
2.UDF查询
SQL查询
如何实现一个SQL解析器

文章插图
select JSON(msg,'query') as query,JSON(msg,'pv') as pv from video_search_query where `partition` in (0) limit 10预览截图:
如何实现一个SQL解析器

文章插图
4.3 ANTLR4 和 Calcite SQL解析对比4.3.1 ANTLR4解析SQLANTLR4解析SQL的主要流程包含:定义词法和语法文件、编写SQL解析逻辑类、主服务调用SQL逻辑类 。
1.定义词法和语法文件
可参考官网提供的开源地址:详情
2.编写SQL解析逻辑类
这里,我们编写一个实现解析SQL表名的类,具体实现代码如下所示:
解析表名
如何实现一个SQL解析器

文章插图
public class TableListener extends antlr4.sql.MySqlParserBaseListener {private String tableName = null;public void enterQueryCreateTable(antlr4.sql.MySqlParser.QueryCreateTableContext ctx) {List<MySqlParser.TableNameContext> tableSourceContexts = ctx.getRuleContexts(antlr4.sql.MySqlParser.TableNameContext.class);for (antlr4.sql.MySqlParser.TableNameContext tableSource : tableSourceContexts) {// 获取表名tableName = tableSource.getText();}}public String getTableName() {return tableName;}}3.主服务调用SQL逻辑类
对实现SQL解析的逻辑类进行调用,具体代码如下所示:
主服务
如何实现一个SQL解析器

文章插图
public class AntlrClient {public static void main(String[] args) {// antlr4 格式化SQLantlr4.sql.MySqlLexer lexer = new antlr4.sql.MySqlLexer(CharStreams.fromString("create table table2 select tid from table1;"));antlr4.sql.MySqlParser parser = new antlr4.sql.MySqlParser(new CommonTokenStream(lexer));// 定义TableListenerTableListener listener = new TableListener();ParseTreeWalker.DEFAULT.walk(listener, parser.sqlStatements());// 获取表名String tableName= listener.getTableName();// 输出表名System.out.println(tableName);}} 4.3.2 Calcite解析SQLCalcite解析SQL的流程相比较ANTLR是比较简单的,开发中无需关注词法和语法文件的定义和编写,只需关注具体的业务逻辑实现 。比如实现一个SQL的COUNT操作,Calcite实现步骤如下所示 。
1.pom依赖
Calcite依赖JAR
如何实现一个SQL解析器

文章插图
如何实现一个SQL解析器

文章插图
<dependencies><!-- 这里对Calcite适配依赖进行封装,引入下列包即可 --><dependency><groupId>org.smartloli</groupId><artifactId>jsql-client</artifactId><version>1.0.0</version></dependency></dependencies> 2.实现代码
Calcite示例代码
如何实现一个SQL解析器

文章插图
package com.vivo.learn.sql.calcite;import com.alibaba.fastjson.JSON;import com.alibaba.fastjson.JSONArray;import com.alibaba.fastjson.JSONObject;import org.smartloli.util.JSqlUtils;public class JSqlClient {public static void main(String[] args) {JSONObject tabSchema = new JSONObject();tabSchema.put("id","integer");tabSchema.put("name","varchar");JSONArray datasets = JSON.parseArray("[{\"id\":1,\"name\":\"aaa\",\"age\":20},{\"id\":2,\"name\":\"bbb\",\"age\":21},{\"id\":3,\"name\":\"ccc\",\"age\":22}]");String tabName = "userinfo";String sql = "select count(*) as cnt from \"userinfo\"";try{String result = JSqlUtils.query(tabSchema,tabName,datasets,sql);System.out.println("result: "+result);}catch (Exception e){e.printStackTrace();}}}3.预览截图
如何实现一个SQL解析器

文章插图
4.3.3 对比结果
如何实现一个SQL解析器

文章插图
综合对比,我们从对两种技术的学习成本、使用复杂度、以及灵活度来对比,可以优先选择Calcite来作为SQL解析器来处理实际的业务需求 。
五、总结另外,在单机模式的情况下,执行计划可以较为简单的翻译成执行代码,但是在分布式领域中 , 因为计算引擎多种多样,因此 , 还需要一个更加贴近具体计算引擎的描述 , 也就是物理计划 。换言之,逻辑计划只是抽象的一层描述,而物理计划则和具体的计算引擎直接挂钩 。

推荐阅读