相关信息

本篇主要学习Druid 对Sql的语法解析。学习完之后,我们可以对任意sql进行解析,同时也可以基于AST语法树来生成sql语句。

(opens new window)

# 一、AST

AST是abstract syntax tree的缩写,也就是抽象语法树。和所有的Parser一样,Druid Parser会生成一个抽象语法树。

在Druid中,AST节点类型主要包括SQLObject、SQLExpr、SQLStatement三种抽象类型。

interface SQLObject {}
interface SQLExpr extends SQLObject {}
interface SQLStatement extends SQLObject {}interface SQLTableSource extends SQLObject {}
class SQLSelect extends SQLObject {}
class SQLSelectQueryBlock extends SQLObject {}
12345678

# 二、语法树解析

# 2.1 核心类介绍

# 2.1.1 SQLStatemment DQL & DML顶级抽象

  • DQL 数据查询语言 select
  • DML 数据操纵语言 insert update delete

最常用的Statement当然是SELECT/UPDATE/DELETE/INSERT,他们分别是

核心类 说明
SQLSelectStatement 查询语句
SQLUpdateStatement 更新语句
SQLDeleteStatement 删除语句
SQLInsertStatement 新增语句
@Test
public void statement() {// 以下全部 trueSystem.out.println(SQLUtils.parseSingleMysqlStatement("select * from users") instanceof SQLSelectStatement);System.out.println(SQLUtils.parseSingleMysqlStatement("insert into users(id,name,age) values (1,'孙悟空',500)") instanceof SQLInsertStatement);System.out.println(SQLUtils.parseSingleMysqlStatement("update users set name = '唐僧' where id = 1 ") instanceof SQLUpdateStatement);System.out.println(SQLUtils.parseSingleMysqlStatement("delete from users where id = 1") instanceof SQLDeleteStatement);
}
12345678

# 2.1.2 SQLSelect SQL查询

SQLSelectStatement包含一个SQLSelect,SQLSelect包含一个SQLSelectQuery。SQLSelectQuery有主要的两个派生类, 分别是SQLSelectQueryBlock(单表sql查询)和SQLUnionQuery(union查询 (opens new window))。

/*** SQLSelectStatement包含一个SQLSelect,SQLSelect包含一个SQLSelectQuery。SQLSelectQuery有主要的两个派生类,* 分别是SQLSelectQueryBlock(单表sql查询)和SQLUnionQuery(联合查询)。*/@Testpublic void SQLSelectQuery() {// trueSystem.out.println(parseSQLSelectQuery("select * from users") instanceof SQLSelectQueryBlock);// trueSystem.out.println(parseSQLSelectQuery("select name from users union select name from school") instanceof SQLUnionQuery);}public SQLSelectQuery parseSQLSelectQuery(String sql) {SQLStatement sqlStatement = SQLUtils.parseSingleMysqlStatement(sql);SQLSelectStatement sqlSelectStatement = Utils.cast(sqlStatement, SQLSelectStatement.class);SQLSelect select = sqlSelectStatement.getSelect();return select.getQuery();}
123456789101112131415161718

# 2.1.3 SQLExpr

SQLExpr 是有几个实现类的。

select id,name,age from users where id = 1 and name = '孙悟空';select u.id, u.name from users as u where id = 1 and name = ?;
123

核心类 举例 说明 适用范围 快速记忆
SQLIdentifierExpr id,name,age SQLIdentifierExpr 查询字段或者where条件 唯一标记
SQLPropertyExpr u.id,u.name 区别于SQLIdentifierExpr,适用于有别名的场景; SQLPropertyExpr.name = id, SQLPropertyExpr.owner = SQLIdentifierExpr = u) 查询字段或者where条件 有别名就是它
SQLBinaryOpExpr id = 1, id > 5 SQLBinaryOpExpr(left = SQLIdentifierExpr = id ,right = SQLValuableExpr = 1) where条件 有操作符就是它
SQLVariantRefExpr id = ? 变量 where条件 有变量符就是它
SQLIntegerExpr id = 1 数字类型 值类型 -
SQLCharExpr name = '孙悟空' 字符类型 值类型 -

# 2.1.3.1 SQLBinaryOpExpr

/*** 操作符相关: SQLBinaryOpExpr*/@Testpublic void SQLBinaryOpExpr() {String sql = "select * from users where id > 1 and age = 18";SQLSelectQuery sqlSelectQuery = Utils.parseSQLSelectQuery(sql);SQLSelectQueryBlock selectQueryBlock = Utils.cast(sqlSelectQuery, SQLSelectQueryBlock.class);SQLExpr where = selectQueryBlock.getWhere();List<SQLObject> conditions = where.getChildren();// [id > 1 , age = 18] 出现了操作符所以是SQLBinaryOpExprfor (SQLObject condition : conditions) {SQLBinaryOpExpr conditionExpr = Utils.cast(condition, SQLBinaryOpExpr.class);SQLBinaryOperator operator = conditionExpr.getOperator();SQLIdentifierExpr conditionColumn = Utils.cast(conditionExpr.getLeft(), SQLIdentifierExpr.class);SQLValuableExpr conditionColumnValue = Utils.cast(conditionExpr.getRight(), SQLValuableExpr.class);Utils.print("条件字段:{},操作符号:{},条件值:{}", conditionColumn.getName(), operator.name, conditionColumnValue);}}
12345678910111213141516171819

# 2.1.3.2 SQLVariantRefExpr

@Testpublic void SQLVariantRefExpr() {String sql = "select * from users where id = ? and name = ?";SQLSelectQuery sqlSelectQuery = Utils.parseSQLSelectQuery(sql);SQLSelectQueryBlock selectQueryBlock = Utils.cast(sqlSelectQuery, SQLSelectQueryBlock.class);SQLExpr where = selectQueryBlock.getWhere();List<SQLObject> conditions = where.getChildren();// [id = ?] 出现了变量符,所以要用SQLVariantRefExprfor (SQLObject condition : conditions) {SQLBinaryOpExpr conditionExpr = Utils.cast(condition, SQLBinaryOpExpr.class);SQLBinaryOperator operator = conditionExpr.getOperator();SQLIdentifierExpr conditionColumn = Utils.cast(conditionExpr.getLeft(), SQLIdentifierExpr.class);SQLVariantRefExpr conditionColumnValue = Utils.cast(conditionExpr.getRight(), SQLVariantRefExpr.class);int index = conditionColumnValue.getIndex();Utils.print("条件字段:{},操作符号:{},索引位:{}", conditionColumn.getName(), operator.name, index);}}
1234567891011121314151617

# 2.1.4 SQLTableSource

常见的SQLTableSource包括SQLExprTableSource、SQLJoinTableSource、SQLSubqueryTableSource、SQLWithSubqueryClause.Entry

核心类 举例 说明 快速记忆
SQLExprTableSource select * from emp where i = 3 name = SQLIdentifierExpr = emp 单表查询
SQLJoinTableSource select * from emp e inner join org o on e.org_id = o.id left = SQLExprTableSource(emp e),right = SQLExprTableSource(org o), condition = SQLBinaryOpExpr(e.org_id = o.id) join 查询使用
SQLSubqueryTableSource select * from (select * from temp) a from(...)是一个SQLSubqueryTableSource 子查询语句
SQLWithSubqueryClause WITH RECURSIVE ancestors AS (SELECT * FROM org UNION SELECT f.* FROM org f, ancestors a WHERE f.id = a.parent_id ) SELECT * FROM ancestors; ancestors AS (...) 是一个SQLWithSubqueryClause.Entry with

# 2.2 SQL语句解析示例

# 2.2.1 解析 Where

注意如果条件语句中只有一个条件,那么where就是一个 SQLBinaryOpExpr。 当条件大于2个,使用 where.getChildren()

/*** 判断where要* 1. 注意是SQLBinaryOpExpr(id = 1) or (u.id = 1) 需要注意是否使用了别名<br>* 2. 注意如果只有一个查询添加 where本身就是一个SQLBinaryOpExpr,如果是多个就要用 where.getChildren()<br></>* 如果有别名: SQLPropertyExpr(name = id , ownerName = u)<br>* 如果没别名: SQLIdentifierExpr(name = id) <br></>* 值对象: SQLValuableExpr** @param where 条件对象*/public static void parseWhere(SQLExpr where) {if (where instanceof SQLBinaryOpExpr) {parseSQLBinaryOpExpr(cast(where, SQLBinaryOpExpr.class));} else {List<SQLObject> childrenList = where.getChildren();for (SQLObject sqlObject : childrenList) {// 包含了 left 和 rightSQLBinaryOpExpr conditionBinary = cast(sqlObject, SQLBinaryOpExpr.class);parseSQLBinaryOpExpr(conditionBinary);}}}public static void parseSQLBinaryOpExpr(SQLBinaryOpExpr conditionBinary) {SQLExpr conditionExpr = conditionBinary.getLeft();SQLExpr conditionValueExpr = conditionBinary.getRight();// 左边有别名所以是SQLPropertyExprif (conditionExpr instanceof SQLPropertyExpr) {SQLPropertyExpr conditionColumnExpr = cast(conditionExpr, SQLPropertyExpr.class);// 右边根据类型进行转换 id是SQLIntegerExpr name是SQLCharExprSQLValuableExpr conditionColumnValue = cast(conditionValueExpr, SQLValuableExpr.class);print("条件列名:{},条件别名:{},条件值:{}", conditionColumnExpr.getName(), conditionColumnExpr.getOwnernName(), conditionColumnValue);}// 如果没有别名if (conditionExpr instanceof SQLIdentifierExpr) {SQLIdentifierExpr conditionColumnExpr = cast(conditionExpr, SQLIdentifierExpr.class);SQLValuableExpr conditionColumnValue = cast(conditionValueExpr, SQLValuableExpr.class);print("条件列名:{},条件值:{}", conditionColumnExpr.getName(), conditionColumnValue);}}
1234567891011121314151617181920212223242526272829303132333435363738394041

# 2.2.2 解析 SQLSelectItem

解析查询的列信息

/*** 解析查询字段,注意是否使用了别名.u.id as userId, u.name as userName, u.age as userAge<br>* userId(sqlSelectItem.getAlias)<br>* 如果有别名: u.id( id = SQLPropertyExpr.getName,u = SQLPropertyExpr.getOwnernName)<br>* 如果没别名: id(id = SQLIdentifierExpr.name)** @param selectColumnList 查询字段*/private void parseSQLSelectItem(List<SQLSelectItem> selectColumnList) {for (SQLSelectItem sqlSelectItem : selectColumnList) {// u.id as userId(selectColumnAlias)String selectColumnAlias = sqlSelectItem.getAlias();// u.id = SQLPropertyExprSQLExpr expr = sqlSelectItem.getExpr();if (expr instanceof SQLPropertyExpr) {SQLPropertyExpr selectColumnExpr = cast(expr, SQLPropertyExpr.class);print("列名:{},别名:{},表别名:{}", selectColumnExpr.getName(), selectColumnAlias, selectColumnExpr.getOwnernName());}if (expr instanceof SQLIdentifierExpr) {SQLIdentifierExpr selectColumnExpr = cast(expr, SQLIdentifierExpr.class);print("列名:{},别名:{}", selectColumnExpr.getName(), selectColumnAlias);}}}
123456789101112131415161718192021222324

# 2.2.3 解析 SQLUpdateSetItem

@Testpublic void SQLUpdateStatement() {SQLStatement sqlStatement = SQLUtils.parseSingleMysqlStatement("update users u set u.name = '唐僧',age = 18 where u.id = 1 ");SQLUpdateStatement sqlUpdateStatement = Utils.cast(sqlStatement, SQLUpdateStatement.class);List<SQLUpdateSetItem> setItems = sqlUpdateStatement.getItems();for (SQLUpdateSetItem setItem : setItems) {SQLExpr column = setItem.getColumn();if (column instanceof SQLPropertyExpr) {SQLPropertyExpr sqlPropertyExpr = Utils.cast(column, SQLPropertyExpr.class);SQLExpr value = setItem.getValue();Utils.print("column:{},列owner:{},value:{}", sqlPropertyExpr.getName(), sqlPropertyExpr.getOwnernName(), value);}if (column instanceof SQLIdentifierExpr) {SQLExpr value = setItem.getValue();Utils.print("column:{},value:{}", column, value);}}SQLExpr where = sqlUpdateStatement.getWhere();Utils.startParse("解析where", Utils::parseWhere, where);}
1234567891011121314151617181920

# 2.2.4 解析 SQLLimit

/*** 偏移量,只有2个值** @param limit 限制*/private void parseLimit(SQLLimit limit) {// 偏移量SQLExpr offset = limit.getOffset();// 便宜数量SQLExpr rowCount = limit.getRowCount();print("偏移量:{},偏移数量:{}", offset, rowCount);}
123456789101112

# 2.2.5 解析 SQLSelectGroupBy

@Testpublic void groupBy() {SQLStatement sqlStatement = SQLUtils.parseSingleMysqlStatement("select name,count(1) as count from users group by name,age having count > 2");SQLSelectStatement selectStatement = Utils.cast(sqlStatement, SQLSelectStatement.class);SQLSelect select = selectStatement.getSelect();SQLSelectQueryBlock query = Utils.cast(select.getQuery(), SQLSelectQueryBlock.class);SQLSelectGroupByClause groupBy = query.getGroupBy();List<SQLExpr> items = groupBy.getItems();for (SQLExpr item : items) {// group by name// group by ageSQLIdentifierExpr groupByColumn = Utils.cast(item, SQLIdentifierExpr.class);Utils.print("group by {}", groupByColumn);}}
12345678910111213141516

# 2.2.6 解析 Having

@Testpublic void having() {SQLStatement sqlStatement = SQLUtils.parseSingleMysqlStatement("select name,count(1) as count from users group by name,age having count > 2");SQLSelectStatement selectStatement = Utils.cast(sqlStatement, SQLSelectStatement.class);SQLSelect select = selectStatement.getSelect();SQLSelectQueryBlock query = Utils.cast(select.getQuery(), SQLSelectQueryBlock.class);SQLSelectGroupByClause groupBy = query.getGroupBy();SQLExpr having = groupBy.getHaving();// 因为只有一个条件,所以having就是SQLBinaryOpExprSQLBinaryOpExpr havingExpr = Utils.cast(having, SQLBinaryOpExpr.class);// 没有使用别名,所以就是SQLIdentifierExprSQLExpr left = havingExpr.getLeft();SQLIdentifierExpr leftExpr = Utils.cast(left, SQLIdentifierExpr.class);// 数字类型就是SQLExpr right = havingExpr.getRight();SQLValuableExpr rightValue = Utils.cast(right, SQLValuableExpr.class);SQLBinaryOperator operator = havingExpr.getOperator();// left:count, operator:>,right:2Utils.print("left:{}, operator:{},right:{}", leftExpr.getName(), operator.name, rightValue.getValue());}
1234567891011121314151617181920

# 三、语法树生成

前面的内容如果都搞清楚了,那么我们就能对sql进行解析,通知可以修改sql解析后的语法树,同时再将修改后的语法树,重新转换成sql

# 3.1 修改语法树

# 3.1.1 增加一个条件

@Testpublic void SQLDeleteStatement(){SQLStatement sqlStatement = SQLUtils.parseSingleMysqlStatement("delete from users where id = 1");SQLDeleteStatement sqlDeleteStatement = Utils.cast(sqlStatement, SQLDeleteStatement.class);sqlDeleteStatement.addCondition(SQLUtils.toSQLExpr("name = '孙悟空'"));
//        DELETE FROM users
//        WHERE id = 1
//        AND name = '孙悟空'System.out.println(SQLUtils.toSQLString(sqlDeleteStatement));}
12345678910

# 3.1.2 修改一个条件值

将条件id = 1 修改成 id = 2

@Testpublic void SQLDeleteStatement2(){SQLStatement sqlStatement = SQLUtils.parseSingleMysqlStatement("delete from users where id = 1");SQLDeleteStatement sqlDeleteStatement = Utils.cast(sqlStatement, SQLDeleteStatement.class);SQLExpr where = sqlDeleteStatement.getWhere();SQLBinaryOpExpr sqlBinaryOpExpr = Utils.cast(where, SQLBinaryOpExpr.class);
//        DELETE FROM users
//        WHERE id = 2sqlBinaryOpExpr.setRight(SQLUtils.toSQLExpr("2"));System.out.println(SQLUtils.toSQLString(sqlDeleteStatement));}
1234567891011

# 四、Visitor模式

访问者模式

所有的AST节点都支持Visitor模式,需要自定义遍历逻辑,可以实现相应的ASTVisitorAdapter派生类

public static class CustomerMySqlASTVisitorAdapter extends MySqlASTVisitorAdapter {private final Map<String, SQLTableSource> ALIAS_MAP = new HashMap<String, SQLTableSource>();private final Map<String, SQLExpr> ALIAS_COLUMN_MAP = new HashMap<String, SQLExpr>();public boolean visit(SQLExprTableSource x) {String alias = x.getAlias();ALIAS_MAP.put(alias, x);return true;}@Overridepublic boolean visit(MySqlSelectQueryBlock x) {List<SQLSelectItem> selectList = x.getSelectList();for (SQLSelectItem sqlSelectItem : selectList) {String alias = sqlSelectItem.getAlias();SQLExpr expr = sqlSelectItem.getExpr();ALIAS_COLUMN_MAP.put(alias, expr);}return true;}public Map<String, SQLTableSource> getAliasMap() {return ALIAS_MAP;}public Map<String, SQLExpr> getAliasColumnMap() {return ALIAS_COLUMN_MAP;}}@Testpublic void AliasVisitor() {String sql = "select u.id as userId, u.name as userName, age as userAge from users as u where u.id = 1 and u.name = '孙悟空' limit 2,10";// 解析SQLSQLStatement sqlStatement = SQLUtils.parseSingleMysqlStatement(sql);CustomerMySqlASTVisitorAdapter customerMySqlASTVisitorAdapter = new CustomerMySqlASTVisitorAdapter();sqlStatement.accept(customerMySqlASTVisitorAdapter);// 表别名:{u=users}System.out.println("表别名:" + customerMySqlASTVisitorAdapter.getAliasMap());// 列别名{userName=u.name, userId=u.id, userAge=age}System.out.println("列别名" + customerMySqlASTVisitorAdapter.getAliasColumnMap());}
123456789101112131415161718192021222324252627282930313233343536373839404142434445

Druid SQL解析相关推荐

  1. sharding-JDBC源码分析(二)SQL解析

    SQL parser SQL解析是根据语法与词法分析SQL,理解SQL含义,才能按照SQL语义处理数据,SQL解析是实现分库分表组件最基础的功能,熟悉Mysql架构的,内部也有很重要的一个模块就是SQ ...

  2. java sql分析器_java sql解析器比较druid sql parser vs jsqlparser vs fdb-sql-parser

    先上结论. 功能上:druid sql parser(支持分区.WITH.DUAL等.使用mysql语法解析时,已知oracle的一些操作符会被转为mysql,如|| 转为OR.使用oracle解析器 ...

  3. jsqlparser mysql_java sql解析器比较druid sql parser vs jsqlparser vs fdb-sql-parser

    先上结论. 功能上:druid sql parser(支持分区.WITH.DUAL等.使用mysql语法解析时,已知oracle的一些操作符会被转为mysql,如|| 转为OR.使用oracle解析器 ...

  4. com.alibaba.druid.sql.parser.ParserException: syntax error, QUES %, pos 80 like报错解决

    最近,把各应用的jdbc连接池统一从dbcp2改成了druid,运行时druid报sql解析错误,如下: select * from test          where 1=1          ...

  5. SQL解析器的性能测试

    对同一个sql语句,使用3种解析器解析出ast语法树(这是编译原理上的说法,在sql解析式可能就是解析器自定义的statement类型),执行100万次的时间对比. package demo.test ...

  6. 机器学习从入门到精通50讲(九)-基于 ANTLR 自己实现一个 SQL 解析器

    一.背景 自2014年大数据首次写入政府工作报告,大数据已经发展7年.大数据的类型也从交易数据延伸到交互数据与传感数据.数据规模也到达了PB级别. 大数据的规模大到对数据的获取.存储.管理.分析超出了 ...

  7. java mysql 语句解析器_几种基于Java的SQL解析工具的比较与调用

    1.sqlparser http://www.sqlparser.com/ 优点:支持的数据库最多,除了传统数据库外还支持hive和greenplum一类比较新的数据库,调用比较方便,功能不错 缺点: ...

  8. SQL解析在美团的应用

    数据库作为核心的基础组件,是需要重点保护的对象.任何一个线上的不慎操作,都有可能给数据库带来严重的故障,从而给业务造成巨大的损失.为了避免这种损失,一般会在管理上下功夫.比如为研发人员制定数据库开发规 ...

  9. Sharding-JDBC 1.5.0.M1 正式发布,全新的 SQL 解析引擎

    经过了长达几个月的紧张开发,Sharding-JDBC 1.5.0.M1终于正式发布.Sharding-JDBC 1.5.0.M1版本是一次里程碑式升级,工作量巨大,Sharding-JDBC截止到1 ...

最新文章

  1. 凭着这把AutoML利剑,这家AI公司荣登 IEEE ISI“铁王座”
  2. 如何制定一个高效的数据保护计划
  3. hdu1074 状态压缩dp+记录方案
  4. 性能测试工具_磁盘性能测试工具fio
  5. erlang精要(7)-模块
  6. 《IBM-PC汇编语言程序设计》(第2版)【沈美明 温冬婵】——第十章——自编解析与答案
  7. mysql 视图sql_SQL的视图
  8. 答题获得思科T-shirt
  9. java 变量与常量_java变量和常量的区别是什么
  10. 非spring托管对象如何获取到spring托管对象
  11. python super()方法的作用_详解python的super()的作用和原理
  12. nodejs前端跨域访问
  13. Diygw拖拽自动生成App
  14. 网站跳转第三方QQ、微信登陆
  15. npm 清理缓存命令 【最新的】
  16. break 和continue 区别以及用法。
  17. c语言五子棋最简单的ai,C++简单五子棋的AI设计实现
  18. 【易康eCognition】面向对象的图像分类学习
  19. 三菱PLC FB库新建和调用-(Gx Work2版本)
  20. iThenticate查重系统的使用及如何去除参考文献

热门文章

  1. 如何把两个视频合成一个?试试这几个视频合并方法
  2. ActionScript学习笔记(四)——滤镜以及画笔的使用
  3. 中英文简单介绍Python关键字 -- Python Key Words
  4. Jquery页面导航(菜单悬停,折叠效果)
  5. TIA博途SCL编程学习2_sin(x)
  6. 陕西农民挖地基,竟然挖出2700年前的青铜家谱!专家:这是中国第一盘
  7. 服务器皮肤显示mod怎么用,配置皮肤 Mod
  8. 六家存储供应商未来一到两年发展方向概述
  9. 如何提高产能?数智化是服装行业的一种趋势
  10. Python入门七:安装支持WinXp运行的Python及环境配置