Durid的SQL解析器浅释
来源:互联网 发布:ipad绘图软件 编辑:程序博客网 时间:2024/04/28 19:13
SQL Parser是Druid的一个重要组成部分,Druid内置使用SQL Parser来实现防御SQL注入(WallFilter)、合并统计没有参数化的SQL(StatFilter的mergeSql)、SQL格式化、分库分表。
1. 各种语法支持
Druid的sql parser是目前支持各种数据语法最完备的SQL Parser。目前对各种数据库的支持如下:
数据库
DML
DDL
odps
完全支持
完全支持
mysql
完全支持
完全支持
oracle
大部分
支持大部分
postgresql
完全支持
支持大部分
sql server
支持常用的
支持常用的ddl
db2
支持常用的
支持常用的ddl
druid还缺省支持sql-92标准的语法,所以也部分支持其他数据库的sql语法。
2. 性能
Druid的SQL Parser是手工编写,性能是antlr、javacc之类工具生成的数倍甚至10倍以上。
SELECT ID, NAME, AGE FROM USER WHERE ID = ?
这样的SQL,druid parser处理大约是5us,也就是每秒中可以处理20万次。
测试代码看这里:
package com.alibaba.druid.benckmark.sql;
import java.util.List;
import junit.framework.TestCase;
import com.alibaba.druid.sql.ast.SQLStatement;
import com.alibaba.druid.sql.dialect.mysql.parser.MySqlStatementParser;
import com.alibaba.druid.sql.dialect.mysql.visitor.MySqlOutputVisitor;
import com.alibaba.druid.sql.test.TestUtils;
import com.alibaba.druid.util.Utils;
public class MySqlPerfTest extends TestCase {
private Stringsql;
protected void setUp() throws Exception {
sql = "SELECT * FROM T";
sql = "SELECT ID, NAME, AGE FROM USER WHERE ID = ?";
// sql = Utils.readFromResource("benchmark/sql/ob_sql.txt");
}
public void test_pert() throws Exception {
for (inti = 0; i < 10; ++i) {
perfMySql(sql);
}
}
long perfMySql(Stringsql) {
long startYGC = TestUtils.getYoungGC();
long startYGCTime = TestUtils.getYoungGCTime();
long startFGC = TestUtils.getFullGC();
long startMillis = System.currentTimeMillis();
for (inti = 0; i < 1000 * 1000; ++i) {
execMySql(sql);
}
long millis = System.currentTimeMillis() - startMillis;
long ygc = TestUtils.getYoungGC() - startYGC;
long ygct = TestUtils.getYoungGCTime() - startYGCTime;
long fgc = TestUtils.getFullGC() - startFGC;
System.out.println("MySql\t" +millis + ", ygc " +ygc + ", ygct "
+ ygct + ", fgc " + fgc);
returnmillis;
}
private String execMySql(Stringsql) {
StringBuilder out = new StringBuilder();
MySqlOutputVisitor visitor =new MySqlOutputVisitor(out);
MySqlStatementParser parser =new MySqlStatementParser(sql);
List<SQLStatement> statementList =parser.parseStatementList();
// for (SQLStatement statement : statementList) {
// statement.accept(visitor);
// visitor.println();
// }
returnout.toString();
}
}
SQLStatement代表一条SQL,解析器会把一个字符串解析成一个SQL的列表,如果字符串中有多个SQL,每个SQL用分号分隔,会返回一个对应个SQLStatement对象的列表。
Visitor类可以定义遇到某个SQL元素后的处理方法,或者遇到某个SQL元素后的处理方法。
3. Druid SQL Parser的代码结构
Druid SQL Parser分三个模块: * Parser * AST *Visitor
解析器通过语法解析和此法解析,把SQL转换成抽象语法树。用户可以使用Visitor模式遍历AST
3.1. Parser
parser是将输入文本转换为ast(抽象语法树),parser有包括两个部分,Parser和Lexer,其中Lexer实现词法分析,Parser实现语法分析。
1. Parser:语法分析器
2. Lexer:词法分析器
3.2. AST
AST是Abstract Syntax Tree的缩写,也就是抽象语法树。AST是parser输出的结果。
3.3. Visitor
Visitor是遍历AST的手段,是处理AST最方便的模式,Visitor是一个接口,有缺省什么都没做的实现VistorAdapter。
我们可以实现不同的Visitor来满足不同的需求,Druid内置提供了如下Visitor:
l OutputVisitor用来把AST输出为字符串
测试用例:com.alibaba.druid.sql.mysql.demo.Demo0
l WallVisitor来分析SQL语意来防御SQL注入攻击
可以使用工具类:WallVisitorUtils
WallVisitorUtils.getValue(SQLUtils.toSQLExpr("1 < 2"))
l ParameterizedOutputVisitor用来合并未参数化的SQL进行统计
测试用例:MySqlParameterizedOutputVisitorTest
可以把一个SQL的参数转换成问号,例如:
SELECT appsheetserialno FROM app_trans WHERE nodeid = _gbk '619'
AND alino = _gbk '2013110900031031001700thfund00163619'
AND apserialno = _gbk '201405120002300002170013205458'
SELECT appsheetserialno
FROM app_trans
WHERE nodeid = ?
AND alino = ?
AND apserialno = ?
l EvalVisitor 用来对SQL表达式求值
有对应的一个工具:SQLEvalVisitorUtils
具体是计算SQL中使用到的各种表达式的值。例如:
SQLEvalVisitorUtils.evalExpr(JdbcConstants.MYSQL,"?>0 && ?>0", 1,0)
SQLEvalVisitorUtils.evalExpr(JdbcConstants.MYSQL,"2>1 && 100>10")
求出表达式的值。
l ExportParameterVisitor用来提取SQL中的变量参数
例如:
String sql = "select id, name from table where id = 100 and name='jluo';";
SQLStatementParser parser = new MySqlStatementParser(sql);
List<SQLStatement> stmtList = parser.parseStatementList();
MySqlExportParameterVisitor pVisitor=new MySqlExportParameterVisitor();
for (SQLStatementstmt : stmtList) {
stmt.accept(pVisitor);
List<Object> list = pVisitor.getParameters();
for (Objectobj : list) {
System.out.println(obj);
}
}
结果:
100
jluo
l SchemaStatVisitor用来统计SQL中使用的表、字段、过滤条件、排序表达式、分组表达式
用来获取SQL中的各种元素,检查SQL中是否含有某些特征值。
有大量测试用例用于测试这个类,例如:MySqlSchemaStatVisitorTest1
例如:
select a.id, a.name FROM users a, money b where a.id=b.id and a.id in (select id from innertables)
可以解析出下面信息:
Tables : {users=Select, money=Select, innertables=Select}
fields : [users.id, users.name, money.id, innertables.id]
select id, name FROM users a
Tables : {users=Select}
fields : [users.id, users.name]
select id, a.name FROM users a
Tables : {users=Select}
fields : [users.id, users.name]
MySQL的Visitor:
$ find ./ -name MySql*Visitor.java
./src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySql2OracleOutputVisitor.java
./src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlASTVisitor.java
./src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlExportParameterVisitor.java
./src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlOutputVisitor.java
./src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlParameterizedOutputVisitor.java
./src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlSchemaStatVisitor.java
./src/main/java/com/alibaba/druid/wall/spi/MySqlWallVisitor.java
3.4. 方言
SQL-92、SQL-99等都是标准SQL,mysql/oracle/pg/sqlserver/odps等都是方言,也就是dialect。parser/ast/visitor都需要针对不同的方言进行特别处理。
- Durid的SQL解析器浅释
- Durid实现SQL监控
- Durid
- Durid其它一切正常,不显示SQL监控
- 使用durid的ConfigFilter对数据库密码加密
- 使用durid的ConfigFilter对数据库密码加密
- java-durid、mybatis、spring 整合基于 AbstractRoutingDataSource 的多数据源读写分离配置
- hive的sql解析
- SQL的解析顺序
- sql的解析过程
- 第一章durid.io简介
- Durid数据池
- durid 关闭时报错
- Druid SQL 解析器的解析过程
- 标准的 SQL 解析顺序
- SQL语句的解析方法
- SQL 语句的解析顺序
- 解析SQL语句的过程
- 程序苏到Dream的进化--Day1
- SQL语句Where中使用别名作为判断条件
- 使用开源项目的正确姿势,都是血和泪的总结!
- 第一个jersey实例
- SQL server 2008R2 提示服务器名称: (local) 错误号: 4060
- Durid的SQL解析器浅释
- PyQt5 - QWidgets部件进阶教程之模拟时钟
- [体系架构][x86]A20地址线的操作
- 微信与支付宝两个平台对接文档
- 冒泡排序 php
- Android Studio变Eclipse风格
- 选择排序 php
- spark&scala集合
- Gradle初识