目录
前言
一、Druid简介
二、Druid SQL Parser
Parser
AST
Visitor
三、血缘功能实现
1.建表语句
1.直接Create+字段定义
2. Create table… as select..
2.插入
1.标准语法
2.高级语法(Multiple Inserts)
3.高级语法(Dynamic Partition Inserts)
点关注,防走丢,如有纰漏之处,请留言指教,非常感谢
前言
之前开发的基于Python语言的sqlparse库开发的SQL语言通用解析工具目前已经开源至github,大家如果有需要可以去看:https://github.com/Fanstuck/SQLblood-relationship。我说过做Python的SQL解析算是一个对AST解析树的深入理解。没想到的是基于sqlparse的工具做出sql解析是可行的,这涉及到较多的递归和判断,但是我写的程序应对的SQL语句应该是不多的1,很多条SQL语句都没有测试完还是有一定的风险的。如果大家有想要解析的SQL可以私信发我,将免费提供SQL解析,如果程序功能和兼容性足够完善的话,将再出一篇文章把所有的解析过程详解。
本篇文章主要讲述的是直接利用Druid的功能直接实现血缘解析,就不再过多的去解析其底层AST树的解析了,大致的做法都是相同的。Druid用于解析sql的工具是本身自带,其主要是数据库连接池实现。
一、Druid简介
Druid 是阿里巴巴开源平台上一个数据库连接池实现,结合了 C3P0、DBCP 等 DB 池的优点,同时加入了日志监控。Druid连接池为监控而生,内置强大的监控功能,监控特性不影响性能。功能强大,能防SQL注入,内置Loging能诊断Hack应用行为。也正是因为有监控SQL注入因此必须要对上交的SQL任务进行解析,获取关键字段。
首先SQL本质上是一种数据处理的描述语言,是一种描述语言的规范。 如果我们用简单字符串处理,使用字符串查找或者正则表达式来提取SQL中的字段,对于简单的SQL可以这样实现,但SQL规范还有复杂的开闭括号以及嵌套查询,复杂SQL几乎不可能通过字符串匹配来实现。因此我们需要将SQL解析。Druid内置的SQL Parser, SQL Parser是Druid的一个重要组成部分,Druid内置使用SQL Parser来实现防御SQL注入(WallFilter)、合并统计没有参数化的SQL(StatFilter的mergeSql)、SQL格式化、分库分表。 而且官方强调:和Antlr生成的SQL有很大不同的是,Druid SQL Parser性能非常好,可以用于生产环境直接对SQL进行分析处理。
通过阅览源码会发现基本主流数据库的SQL语句都支持解析:
数据库 | DML | DDL |
---|---|---|
odps | 完全支持 | 完全支持 |
mysql | 完全支持 | 完全支持 |
postgresql | 完全支持 | 完全支持 |
oracle | 支持大部分 | 支持大部分 |
sql server | 支持常用的 | 支持常用的ddl |
db2 | 支持常用的 | 支持常用的ddl |
hive | 支持常用的 | 支持常用的ddl |
每个数据库都有自己对应的AST树解析、parser语法解析和visitor模式。个别几个数据库的解析较为特殊,比如Hive、mysql等带额外带有其他的功能。
二、Druid SQL Parser
Druid SQL Parser源码中主要的构成框架包括:Parser、AST和Visitor。
Parser
根据之前的研究我们清楚语法分析器(Parser):将上一步得到的Token流转换为语法定义的树结构。对于HiveSQL的解析来讲,对于其定义的grammar语法文件来看,其各个不同的语法解析文件就是其SQL执行过程的支撑,自然需要先解析获取其对应的语法结构:
From的解析文件可以说是通用的,因此在parser并没有看到关于Hive的From文件,都统一由全局SQLParser获取。
这些特定数据库的类都全部由通用parser继承而来,添加新方法。
AST
AST是abstract syntax tree的缩写,也就是抽象语法树。和所有的Parser一样,Druid Parser会生成一个抽象语法树。
之所以说语法是“抽象”的,是因为这里的语法并不会表示出真实语法中出现的每个细节。比如,嵌套括号被隐含在树的结构中,并没有以节点的形式呈现;而类似于if-condition-then这样的条件跳转语句,可以使用带有两个分支的节点来表示。
和抽象语法树相对的是具体语法树。一般的,在源代码的翻译和编译过程中,语法分析器创建出分析树。一旦AST被创建出来,在后续的处理过程中,比如语义分析阶段,会添加一些信息。
String sql_format=formatMysql(sql_4);final DbType dbType = JdbcConstants.HIVE;// SQLStatement就是ASTList stmtList = SQLUtils.parseStatements(sql_4, dbType);System.out.println(stmtList);
在Druid中,AST节点类型主要包括SQLObject、SQLExpr、SQLStatement三种抽象类型。
官方文档解释的更加清楚:Druid_SQL_AST
package com.alibaba.druid.sql.ast.expr;// SQLName是一种的SQLExpr的Expr,包括SQLIdentifierExpr、SQLPropertyExpr等public interface SQLName extends SQLExpr {}// 例如 ID = 3 这里的ID是一个SQLIdentifierExprclass SQLIdentifierExpr implements SQLExpr, SQLName { String name;} // 例如 A.ID = 3 这里的A.ID是一个SQLPropertyExprclass SQLPropertyExpr implements SQLExpr, SQLName { SQLExpr owner; String name;} // 例如 ID = 3 这是一个SQLBinaryOpExpr// left是ID (SQLIdentifierExpr)// right是3 (SQLIntegerExpr)class SQLBinaryOpExpr implements SQLExpr { SQLExpr left; SQLExpr right; SQLBinaryOperator operator;}// 例如 select * from where id = " />SQL_Parser_Demo_visitor 三、血缘功能实现
1.建表语句
关于建表SQL语句一般包括一下两种常见方式,以Hive建表语句为例:
1.直接Create+字段定义
CREATE EXTERNAL TABLE dwd_database.table_name( id BIGINT,user_id STRING,gmt_modified TIMESTAMP,gmt_create TIMESTAMP,pending_reward INT,description STRING)PARTITIONED BY ( pt STRING )row format delimited fields terminated by '\t'STORED AS TEXTFILElocation 'hdfs://nameservice1/user/hive/warehouse/dwd_database.db/table_name';
解析结果为:
2. Create table... as select..
这个存在多重嵌套select,涉及到表和字段。如:
create table table_name as select * from t_table_name where pt='20210829';
解析结果为:
2.插入
1.标准语法
INSERT OVERWRITE TABLE tablename [PARTITION (partcol1=val1, partcol2=val2 ...)] select_statement1 FROM from_statement;INSERT INTO TABLE tablename [PARTITION (partcol1=val1, partcol2=val2 ...)] select_statement1 FROM from_statement;INSERT INTO TABLE tablename [PARTITION (partcol1=val1, partcol2=val2 ...)](z, y) select_statement1 FROM from_statement;
2.高级语法(Multiple Inserts)
FROM from_statementINSERT OVERWRITE TABLE tablename [PARTITION (partcol1=val1, partcol2=val2 ...)] select_statement1[INSERT OVERWRITE TABLE tablename2 [PARTITION ...] select_statement2][INSERT INTO TABLE tablename2 [PARTITION ...] select_statement2];
3.高级语法(Dynamic Partition Inserts)
INSERT OVERWRITE TABLE tablename PARTITION (partcol1[=val1], partcol2[=val2] ...) select_statement FROM from_statement;INSERT INTO TABLE tablename PARTITION (partcol1[=val1], partcol2[=val2] ...) select_statement FROM from_statement;
解析和Create差不多直接代入功能就好了:
这里我没有写那么多可以自行添加。好了先写这么多,内容已经足够多了,下篇文章将继续完善基础功能。
点关注,防走丢,如有纰漏之处,请留言指教,非常感谢
以上就是本期全部内容。我是fanstuck ,有问题大家随时留言讨论 ,我们下期见