Druid学习笔记 01、快速了解Druid中SqlParser实现

文章目录

  • 前言
  • 介绍Druid
  • 代码目录介绍
  • 模块一:Parser
  • 模块二:Druid_SQL_AST
    • 在Druid SQL Parser中有哪些AST节点类型?
    • 熟悉常用的AST节点组成
      • 常用的SQLExpr有哪些?
      • 常用的SQLStatemment?
      • SQLTableSource
      • SQLSelect & SQLSelectQuery
      • SQLCreateTableStatement
    • 怎样产生AST
      • 通过SQLUtils产生List<SQLStatement>
      • 通过SQLUtils产生SQLExpr
    • 怎样打印AST节点?
      • 通过SQLUtils工具类打印节点
    • 如何自定义遍历AST节点?
  • 模块三:Visitor
    • 案例:统计SQL中涉及到的表、字段
  • 参考文章
  • 资料获取

Druid学习笔记 01、快速了解Druid中SqlParser实现

前言

博主介绍:✌目前全网粉丝4W+,csdn博客专家、Java领域优质创作者,博客之星、阿里云平台优质作者、专注于Java后端技术领域。

涵盖技术内容:Java后端、大数据、算法、分布式微服务、中间件、前端、运维等。

博主所有博客文件目录索引:博客目录索引(持续更新)

CSDN搜索:长路

视频平台:b站-Coder长路

介绍Druid

Druid是数据库连接池,能够提供强大的监控和扩展功能。

SQL Parser是Druid的一个重要组成部分,Druid内置使用SQL Parser来实现防御SQL注入(WallFilter)、合并统计没有参数化的SQL(StatFilter的mergeSql)、SQL格式化、分库分表。

Druid SQL Parser分三个模块:Parser,AST,Visitor。

  • parser是将输入文本转换为ast(抽象语法树),parser有包括两个部分,Parser和Lexer,其中Lexer实现词法分析,Parser实现语法分析。
  • AST是Abstract Syntax Tree的缩写,也就是抽象语法树。
  • Visitor是遍历AST的手段,是处理AST最方便的模式。

生成语法树过程:

生成语法树一般过程:字符流->词法解析器->token流->语法解析器->语法树。
对于字符流经过词法解析器后变成token流,如 SELECT A.ID,B.ID FROM A 将变成如下的token流。

字符类型
SELECT关键字
A变量
.
ID变量
,逗号
B变量
.ID
FROM关键字
A变量

接下来语法解析器根据上面的token流生成一棵语法树:

img

代码目录介绍

img

  • com.alibaba.druid.sql.ast.* 为通用 ast
  • com.alibaba.druid.sql.dialect.* 为方言 ast,如oracle,mysql

img

  • *.expr为表达式

*.clause为子句

*.stmt为分析后的结果

*.parser为解析类,*StatementParser为主分析类

.visitor 为AST visitor类,注意其中OutputVisitor为输出,stmt.toString实际调用此类,注意分析后输出调用toString的输出不一定是原始sql

Lexer语义

说明:增加新的类型可以寻找类似语法来实现,一般会牵涉到StatementParser及OutputVisitor修改

模块一:Parser

parser是将输入文本转换为ast(抽象语法树),parser有包括两个部分,Parser和Lexer,其中Lexer实现词法分析,Parser实现语法分析。

模块二:Druid_SQL_AST

wiki学习:https://github.com/alibaba/druid/wiki/Druid_SQL_AST#2-%E5%9C%A8druid-sql-parser%E4%B8%AD%E6%9C%89%E5%93%AA%E4%BA%9Bast%E8%8A%82%E7%82%B9%E7%B1%BB%E5%9E%8B

在Druid SQL Parser中有哪些AST节点类型?

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

package com.alibaba.druid.sql.ast;interface SQLObject {} 
interface SQLExpr extends SQLObject {} // 条件表达式相关的抽象,例如 ID = 3 这里的ID是一个SQLIdentifierExpr
interface SQLStatement extends SQLObject {} //最常用的Statement当然是SELECT/UPDATE/DELETE/INSERT,他们分别是SQLSelectStatement ,SQLUpdateStatement ,SQLDeleteStatement ,SQLInsertStatement interface SQLTableSource extends SQLObject {} //常见的SQLTableSource包括SQLExprTableSource、SQLJoinTableSource、SQLSubqueryTableSource、SQLWithSubqueryClause.Entry
class SQLSelect extends SQLObject {}
class SQLSelectQueryBlock extends SQLObject {} //SQLSelectStatement包含一个SQLSelect,SQLSelect包含一个SQLSelectQuery,都是组成的关系。SQLSelectQuery有主要的两个派生类,分别是SQLSelectQueryBlock和SQLUnionQuery。

熟悉常用的AST节点组成

常用的SQLExpr有哪些?

package com.alibaba.druid.sql.ast.expr;// SQLName是一种的SQLExpr的Expr,包括SQLIdentifierExpr、SQLPropertyExpr等
public interface SQLName extends SQLExpr {}// 例如 ID = 3 这里的ID是一个SQLIdentifierExpr
class SQLIdentifierExpr implements SQLExpr, SQLName {String name;
} // 例如 A.ID = 3 这里的A.ID是一个SQLPropertyExpr
class 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 = ?,这里的?是一个SQLVariantRefExpr,name是'?'
class SQLVariantRefExpr extends SQLExprImpl { String name;
}// 例如 ID = 3 这里的3是一个SQLIntegerExpr
public class SQLIntegerExpr extends SQLNumericLiteralExpr implements SQLValuableExpr { Number number;// 所有实现了SQLValuableExpr接口的SQLExpr都可以直接调用这个方法求值@Overridepublic Object getValue() {return this.number;}
}// 例如 NAME = 'jobs' 这里的'jobs'是一个SQLCharExpr
public class SQLCharExpr extends SQLTextLiteralExpr implements SQLValuableExpr{String text;
}

常用的SQLStatemment?

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

package com.alibaba.druid.sql.ast.statement;class SQLSelectStatement implements SQLStatement {SQLSelect select;
}
class SQLUpdateStatement implements SQLStatement {SQLExprTableSource tableSource;List<SQLUpdateSetItem> items;SQLExpr where;
}
class SQLDeleteStatement implements SQLStatement {SQLTableSource tableSource; SQLExpr where;
}
class SQLInsertStatement implements SQLStatement {SQLExprTableSource tableSource;List<SQLExpr> columns;SQLSelect query;
}

SQLTableSource

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

class SQLTableSourceImpl extends SQLObjectImpl implements SQLTableSource { String alias;
}// 例如 select * from emp where i = 3,这里的from emp是一个SQLExprTableSource
// 其中expr是一个name=emp的SQLIdentifierExpr
class SQLExprTableSource extends SQLTableSourceImpl {SQLExpr expr;
}// 例如 select * from emp e inner join org o on e.org_id = o.id
// 其中left 'emp e' 是一个SQLExprTableSource,right 'org o'也是一个SQLExprTableSource
// condition 'e.org_id = o.id'是一个SQLBinaryOpExpr
class SQLJoinTableSource extends SQLTableSourceImpl {SQLTableSource left;SQLTableSource right;JoinType joinType; // INNER_JOIN/CROSS_JOIN/LEFT_OUTER_JOIN/RIGHT_OUTER_JOIN/...SQLExpr condition;
}// 例如 select * from (select * from temp) a,这里第一层from(...)是一个SQLSubqueryTableSource
SQLSubqueryTableSource extends SQLTableSourceImpl {SQLSelect select;
}/* 
例如
WITH RECURSIVE ancestors AS (SELECT *FROM orgUNIONSELECT f.*FROM org f, ancestors aWHERE f.id = a.parent_id
)
SELECT *
FROM ancestors;这里的ancestors AS (...) 是一个SQLWithSubqueryClause.Entry
*/
class SQLWithSubqueryClause {static class Entry extends SQLTableSourceImpl { SQLSelect subQuery;}
}

SQLSelect & SQLSelectQuery

SQLSelectStatement包含一个SQLSelect,SQLSelect包含一个SQLSelectQuery,都是组成的关系。SQLSelectQuery有主要的两个派生类,分别是SQLSelectQueryBlock和SQLUnionQuery。

class SQLSelect extends SQLObjectImpl { SQLWithSubqueryClause withSubQuery;SQLSelectQuery query;
}interface SQLSelectQuery extends SQLObject {}class SQLSelectQueryBlock implements SQLSelectQuery {List<SQLSelectItem> selectList;SQLTableSource from;SQLExprTableSource into;SQLExpr where;SQLSelectGroupByClause groupBy;SQLOrderBy orderBy;SQLLimit limit;
}class SQLUnionQuery implements SQLSelectQuery {SQLSelectQuery left;SQLSelectQuery right;SQLUnionOperator operator; // UNION/UNION_ALL/MINUS/INTERSECT
}

SQLCreateTableStatement

建表语句包含了一系列方法,用于方便各种操作

public class SQLCreateTableStatement extends SQLStatementImpl implements SQLDDLStatement, SQLCreateStatement {SQLExprTableSource tableSource;List<SQLTableElement> tableElementList;Select select;// 忽略大小写的查找SQLCreateTableStatement中的SQLColumnDefinitionpublic SQLColumnDefinition findColumn(String columName) {}// 忽略大小写的查找SQLCreateTableStatement中的column关联的索引public SQLTableElement findIndex(String columnName) {}// 是否外键依赖另外一个表public boolean isReferenced(String tableName) {}
}

怎样产生AST

通过SQLUtils产生List

import com.alibaba.druid.util.JdbcConstants;String dbType = JdbcConstants.MYSQL;
List<SQLStatement> statementList = SQLUtils.parseStatements(sql, dbType);

通过SQLUtils产生SQLExpr

String dbType = JdbcConstants.MYSQL;
SQLExpr expr = SQLUtils.toSQLExpr("id=3", dbType);

怎样打印AST节点?

通过SQLUtils工具类打印节点

package com.alibaba.druid.sql;public class SQLUtils {// 可以将SQLExpr/SQLStatement打印为String类型static String toSQLString(SQLObject sqlObj, String dbType);// 可以将一个&lt;SQLStatement&gt;打印为String类型static String toSQLString(List<SQLStatement> statementList, String dbType);
}

如何自定义遍历AST节点?

所有的AST节点都支持Visitor模式,需要自定义遍历逻辑,可以实现相应的ASTVisitorAdapter派生类,比如 https://github.com/alibaba/druid/wiki/SQL_Parser_Demo_visitor

模块三:Visitor

Visitor是遍历AST的手段,是处理AST最方便的模式,Visitor是一个接口,有缺省什么都没做的实现VistorAdapter。

Druid内置提供了如下Visitor:

  • OutputVisitor用来把AST输出为字符串
  • WallVisitor 来分析SQL语意来防御SQL注入攻击
  • ParameterizedOutputVisitor用来合并未参数化的SQL进行统计
  • EvalVisitor 用来对SQL表达式求值
  • ExportParameterVisitor用来提取SQL中的变量参数
  • SchemaStatVisitor 用来统计SQL中使用的表、字段、过滤条件、排序表达式、分组表达式
  • SQL格式化 Druid内置了基于语义的SQL格式化功能

Druid提供了多种默认实现的Visitor,可以满足基本需求,如果默认提供的不满足需求,可自行实现自定义Visitor。

案例:统计SQL中涉及到的表、字段

比如我们要统计下一条SQL中涉及了哪些表 select name ,id ,select money from user from acct where id =10,如果我们不用visitor,自行遍历AST,能实现,但是很繁琐。

但是我们用默认自带的Visitor就可以很轻松的实现:MySqlSchemaStatVisitor

package com.changlu.visitor;import com.alibaba.druid.sql.ast.SQLStatement;
import com.alibaba.druid.sql.dialect.mysql.parser.MySqlStatementParser;
import com.alibaba.druid.sql.dialect.mysql.visitor.MySqlSchemaStatVisitor;
import com.alibaba.druid.sql.parser.SQLStatementParser;/*** 验证MySqlSchemaStatVisitor*/
public class TestSchemaVisitor {public static void main(String[] args) {SQLStatementParser parser = new MySqlStatementParser("select  name ,id ,select money from user from acct where id =10");SQLStatement sqlStatement = parser.parseStatement();// 定义visitorMySqlSchemaStatVisitor visitor = new MySqlSchemaStatVisitor();// 执行visitor访问操作sqlStatement.accept(visitor);// 获取到最终的字段名、tables、条件分支、db类型System.out.println(visitor.getColumns()); //[acct.name, acct.id, user.money]System.out.println(visitor.getTables()); //{acct=Select, user=Select}System.out.println(visitor.getConditions()); //[acct.id = 10]System.out.println(visitor.getDbType());//mysql}}

img


参考文章

[1]. 分析Druid 连接池中SQL语法树的基本原理:https://zhuanlan.zhihu.com/p/411029742

[2]. 使用Druid SQL Parser解析SQL:https://blog.csdn.net/cckevincyh/article/details/125317977

[3]. Ali Druid Parser AST扩展及修改记录:https://blog.csdn.net/weixin_40455124/article/details/91488294

[4]. SQL解析在美团的应用:https://tech.meituan.com/2018/05/20/sql-parser-used-in-mtdp.html

资料获取

大家点赞、收藏、关注、评论啦~

精彩专栏推荐订阅:在下方专栏👇🏻

  • 长路-文章目录汇总(算法、后端Java、前端、运维技术导航):博主所有博客导航索引汇总
  • 开源项目Studio-Vue—校园工作室管理系统(含前后台,SpringBoot+Vue):博主个人独立项目,包含详细部署上线视频,已开源
  • 学习与生活-专栏:可以了解博主的学习历程
  • 算法专栏:算法收录

更多博客与资料可查看👇🏻获取联系方式👇🏻,🍅文末获取开发资源及更多资源博客获取🍅

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/94716.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/94716.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Rust中生命周期的理解与应用

在学习Rust编程语言时,理解生命周期(Lifetime)是非常关键的,因为它直接影响到代码的安全性和性能。今天我们来深入探讨Rust中的一个常见问题——生命周期的误解和正确应用,结合实际代码实例来说明。 生命周期的基本概念 Rust中的生命周期是用来确保引用(Reference)在其…

智慧感知新体验:英飞凌雷达在智能家居的创新应用

随着智慧家居快速发展&#xff0c;感知技术成为实现高效、便捷生活的关键。雷达作为非接触、高精度的感测方案&#xff0c;正在家居应用中展现出巨大潜力。 本次研讨会将由英飞凌大中华区雷达应用产品经理 Tommy Wan主讲&#xff0c;分享他在智能门铃、门锁与安防摄像头等应用…

AI:新书预告—从机器学习避坑指南(分类/回归/聚类/可解释性)到大语言模型落地手记(RAG/Agent/MCP),一场耗时5+3年的技术沉淀—“代码可跑,经验可抄”—【一个处女座的程序猿】携两本AI

AI&#xff1a;新书预告—从机器学习避坑指南(分类/回归/聚类/可解释性)到大语言模型落地手记(RAG/Agent/MCP)&#xff0c;一场耗时53年的技术沉淀—“代码可跑&#xff0c;经验可抄”—【一个处女座的程序猿】携两本AI实战书终于正式来了&#xff01; 导读&#xff1a;大家好&…

数据结构:栈、队列

一、栈和队列与链表的区别1.链表可以在任意位置插入和删除元素2.栈和队列只允许在指定位置插入和删除元素3.栈只允许在栈顶位置入栈和出栈元素3.相同点&#xff1a;表、栈、队列都是一种线性结构&#xff08;一对一&#xff09;4.栈和队列是一种特殊的表状结构二、栈&#xff0…

cuda编程笔记(13)--使用CUB库实现基本功能

CUB 是 NVIDIA 提供的 高性能 CUDA 基础库&#xff0c;包含常用的并行原语&#xff08;Reduction、Scan、Histogram 等&#xff09;&#xff0c;可以极大简化代码&#xff0c;并且比手写版本更优化。CUB无需链接&#xff0c;只用包含<cub/cub.cuh>头文件即可需要先临时获…

LabVIEW滤波器测控系统

​基于LabVIEW 平台的高频滤波器测控系统&#xff0c;通过整合控制与测试功能&#xff0c;替代传统分离式测控模式。系统以 LabVIEW 为核心&#xff0c;借助标准化接口实现对滤波器的自动化参数调节与性能测试&#xff0c;显著提升测试效率与数据处理能力&#xff0c;适用于高频…

美团运维面试题及参考答案(上)

输入一个字符串,将其转换成数字时,需要考虑哪些情况(如字符串是否合法、是否为空、int 的范围、是否为 16 进制等)? 将字符串转换成数字时,需全面考虑多种边界情况和合法性问题,具体如下: 字符串基础状态:首先需判断字符串是否为空(长度为0)或仅包含空白字符(如空…

Spring-AI 深度实战:企业级 AI 应用开发指南与 Python 生态对比(高级篇)

为什么 Spring-AI 是企业级 AI 的“隐形冠军”&#xff1f;&#xff08;而不仅是另一个封装库&#xff09;在 Python 主导的 AI 世界中&#xff0c;Spring-AI 的诞生常被误解为“Java 的跟风之作”。但真正的企业级 AI 需求&#xff08;事务一致性、分布式追踪、安全审计&#…

OpenAI 回归开源领域突发两大推理模型,六强AI企业竞逐加剧军备竞赛态势!

获悉&#xff0c;OpenAI重回开源赛道&#xff0c;奥特曼深夜官宣两个分别名为GPT-oss-120b和GPT-oss-20b的模型将在AI软件托管平台Hugging Face上线&#xff0c;在用户输入指令后将能生成文本。两大推理模型上线GPT-oss-120b适用于需要高推理能力的生产级和通用型场景。在核心推…

嵌入式学习硬件(一)ARM体系架构

目录 1.SOC 2.内核架构的分类 3.冯诺依曼架构和哈佛架构 4.kernel 5.指令集 6.ARM处理器产品分类 7.编译的四个步骤​编辑 8.RAM和ROM​编辑 9.ARM处理器工作模式 10.异常处理 11.CPSR程序状态寄存器 1.SOC system on chip 片上系统&#xff0c;可以运行操作系统的一种高端的功…

OpenAI推出开源GPT-oss-120b与GPT-oss-20b突破性大模型,支持商用与灵活部署!

模型介绍OpenAI再次推出开源模型&#xff0c;发布了两款突破性的GPT-oss系列大模型&#xff0c;即GPT-oss-120b和GPT-oss-20b&#xff0c;为AI领域带来了巨大的创新和发展潜力。这两款模型不仅在性能上与现有的闭源模型媲美&#xff0c;而且在硬件适配性上具有明显优势&#xf…

【Unity Plugins】使用ULipSync插件实现人物唇形模拟

一、下载插件ULipSync&#xff1a; 1. 进入Github网址&#xff1a;https://github.com/hecomi/uLipSync/releases/tag/v3.1.4 2. 点击下载下方的unitypackage 3. 安装使用ULipSync的相关的插件 发行者也提到了&#xff0c;在使用的时候需要在Package Manager里安装Unity.B…

基于 Transformer-BiGRU GlobalAttention-CrossAttention 的并行预测模型

1 背景与动机 在高频、多尺度且非平稳的时序场景(如新能源产能预测、金融行情、用户行为流分析)中,单一网络分支 往往难以同时捕获 长程依赖(Transformer 长距离建模优势) 局部细粒信息(循环网络对短期波动敏感) 将 Transformer 与 双向 GRU(BiGRU) 以并行支路组合…

大模型与Spring AI的无缝对接:从原理到实践

摘要&#xff1a;本文系统梳理了大模型知识&#xff0c;以及与Spring AI的集成方案&#xff0c;涵盖本地部署、云服务、API调用三种模式的技术选型对比。通过DeepSeek官方API示例详解Spring AI的四种开发范式&#xff08;纯Prompt/Agent/RAG/微调&#xff09;&#xff0c;并提供…

linux下实现System V消息队列实现任意结构体传输

以下是一个实现&#xff0c;可以发送和接收任意类型的结构体消息&#xff0c;而不仅限于特定的CustomMsg类型&#xff1a;#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/ipc.h> #include <sys/msg.h> #include <…

TCP的三次握手和四次挥手实现过程。以及为什么需要三次握手?四次挥手?

三次握手和四次挥手的实现原理&#xff0c;以及为什么要这样设计&#xff1f;三次握手的实现三次握手的核心角色与参数三次握手的具体步骤第一步&#xff1a;客户端 → 服务器&#xff08;发送 SYN 报文&#xff09;第二步&#xff1a;服务器 → 客户端&#xff08;发送 SYNACK…

Java开发时出现的问题---架构与工程实践缺陷

除语言和并发层面&#xff0c;代码设计、工程规范的缺陷更易导致系统扩展性差、维护成本高&#xff0c;甚至引发线上故障。1. 面向对象设计的常见误区过度继承与脆弱基类&#xff1a;通过继承复用代码&#xff08;如class A extends B&#xff09;&#xff0c;会导致子类与父类…

项目评审管理系统(源码+文档+讲解+演示)

引言 在当今快速发展的商业环境中&#xff0c;项目评审和管理是确保项目成功的关键环节。项目评审管理系统作为一种创新的数字化工具&#xff0c;通过数字化手段优化项目评审和管理的全流程&#xff0c;提高项目管理效率&#xff0c;降低风险&#xff0c;提升项目成功率。本文将…

ComfyUI 安装WanVideoWrapper

目录 方法2&#xff1a;通过 ComfyUI-Manager 安装 方法3&#xff1a;手动下载并解压 测试代码&#xff1a; WanVideoWrapper 方法2&#xff1a;通过 ComfyUI-Manager 安装 在 ComfyUI 界面顶部找到 Manager&#xff08;管理器&#xff09;选项。 进入 Install Custom Nod…

react合成事件大全,如onClick,onDrag

1. 鼠标事件onClick - 点击事件onContextMenu - 右键菜单事件onDoubleClick - 双击事件onDrag - 拖拽事件onDragEnd - 拖拽结束事件onDragEnter - 拖拽进入目标区域事件onDragExit - 拖拽离开目标区域事件onDragLeave - 拖拽离开事件onDragOver - 拖拽悬停事件onDragStart - 拖…