手写MySQL补充章(十二)SQL语法解析之语法树

news2024/11/28 16:41:40

目录

模块分析

AST节点类型

SQL词法解析

举个例子


之前写的在第九章写的sql解析太简单了,SQL规范还有复杂的开闭括号以及嵌套查询,复杂SQL几乎不可能通过字符串匹配来实现。

本章以Druid SQL Parser解析SQL为例,进行分析。

模块分析

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

parser有包括两个部分,Parser和Lexer,其中Lexer实现词法分析,Parser实现语法分析。

Druid Parser会生成一个AST抽象语法树

Visitor是遍历AST的手段,最后返回json形式结果

在使用之前不要忘记了加入依赖

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.2.6</version>
    <scope>test</scope>
</dependency>

AST节点类型

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

  • interface SQLObject {} 
  • interface SQLExpr extends SQLObject {} // 条件表达式相关的抽象,例如select * from  table where ID = 3 这里的ID是一个SQLIdentifierExpr
  • interface SQLStatement extends SQLObject {} //最常用的Statement当然是SELECT/UPDATE/DELETE/INSERT

SQL词法解析

我这里主要关注在SQLExpr, 因为这个跟条件表达式相关的解析。

常用的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都可以直接调用这个方法求值
    @Override
    public Object getValue() {
        return this.number;
    }
}

// 例如 NAME = 'jobs' 这里的'jobs'是一个SQLCharExpr
public class SQLCharExpr extends SQLTextLiteralExpr implements SQLValuableExpr{
    String text;
}

举个例子

对以下代码进行调试

package src;

import com.alibaba.druid.sql.SQLUtils;
import com.alibaba.druid.sql.ast.SQLStatement;
import com.alibaba.druid.util.JdbcConstants;

import java.util.List;

public class main {
    public static void main(String[] args) {
        String sql = "select * from t where id=1 or name='test' and age=14";
        List<SQLStatement> sqlStatements = SQLUtils.parseStatements(sql, JdbcConstants.MYSQL);
        System.out.println(sqlStatements);
    }
}

可以得到 

 从最终的结果可以看出来,其实就是一个二叉树,父结点就是一个操作符,然后左右孩子结点就是表达式的左右两边的字段名和对应的值。

而且还可以通过SQLUtils.toSQLString打印节点

SQLExpr sqlExpr = SQLUtils.toSQLExpr("(id=1 or name='test' and age=14)", JdbcConstants.MYSQL);
System.out.println(SQLUtils.toSQLString(sqlExpr, JdbcConstants.MYSQL));
//id = 1
//OR name = 'test'
//AND age = 14

看到这里我们是不是有一点点思路了,前面我们说SQLUtils产生SQLExpr本质上就是一个二叉树,所以我们可以通过遍历二叉树的方式去获取每个结点,判断结点的类型,然后在把它转成一个我们JSON的一个对象。

那要遍历二叉树,很显然我们这里需要用后序遍历的方式,因为我想从最下往上去遍历,最后遍历根结点,才能把左右两棵树通过操作符合并起来。

可以简单的把树画出来

从图上看出来我们遍历左子树,在遍历condition 1这部分的子树的时候,先遍历ID和1,然后再遍历到父节点的=,叶子节点我们可以不看,我们只要判断到节点是SQLBinaryOperator,我们就可以把他们的左右节点拿出来构成出一个condition 1对象,一样的我们会遍历右子树,遍历出condition 2和condition 3两个对象,然后我们在遍历他们的父节点OR,这个时候我们只需要把它左右子树的两个condition 2 和condition 3放到list中,然后在给他加上一个operator 为OR即可变成一个新的condition 4。然后最后遍历到根结点,就把condition 1 和 condition 4通过AND连接变成一个condition 5,而这个condition 5就是我们最终的JSON结构了。

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

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

相关文章

如何做好需求管理?经验方法、模型、工具

需求管理能力是衡量产品经理能力的一个重要指标。因为需求是产品的基石&#xff0c;只有选取恰当的方法进行需求分析及管理&#xff0c;才能更好的构建产品方案&#xff0c;从而输出精准的产品定义。结合本人学习和自身经验&#xff0c;打算将需求管理分”需求挖掘”、”需求分…

102.第十九章 MySQL数据库 -- MySQL的备份和恢复(十二)

5.备份和恢复 5.1 备份恢复概述 5.1.1 为什么要备份 灾难恢复:硬件故障、软件故障、自然灾害、黑客攻击、误操作测试等数据丢失场景 参考链接: https://www.toutiao.com/a6939518201961251359/ 5.1.2 备份类型 完全备份,部分备份 完全备份:整个数据集 部分备份:只备份数…

shell的环境变量

一、什么是环境变量 环境变量由系统提前创建的&#xff0c;不仅在Shell编程方面&#xff0c;而且在Linux系统管理方面&#xff0c;都起着非常重要的作用。 打个比方&#xff0c;我们平时所用的编程语言如c语言&#xff0c;我们都会碰到变量的作用域的问题。比如在函数中 定义的…

新的一年软件测试行业的趋势能够更好?

如果说&#xff0c;2022年对于全世界来说&#xff0c;都是一场极大的挑战的话&#xff1b;那么&#xff0c;2023年绝对是机遇多多的一年。众所周知&#xff0c;随着疫情在全球范围内逐步得到控制&#xff0c;无论是国际还是国内的环境&#xff0c;都会呈现逐步回升的趋势&#…

【Redis高级-集群分片】

单机安装Redis首先需要安装Redis所需要的依赖&#xff1a;yum install -y gcc tclRedis安装包上传到虚拟机的任意目录&#xff1a;我放到了/tmp目录&#xff1a;解压缩&#xff1a;tar -zxvf /tmp/redis-6.2.4.tar.gz -C /tmp解压后&#xff1a;进入redis目录&#xff1a;cd /t…

剑指 Offer 41. 数据流中的中位数

题目 如何得到一个数据流中的中位数&#xff1f;如果从数据流中读出奇数个数值&#xff0c;那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值&#xff0c;那么中位数就是所有数值排序之后中间两个数的平均值。 例如&#xff0c;[2,3,4] 的中位数是…

从零开始的python基础教程(2)

九、Python Standard Library 1、Paths from pathlib import Path# Windows Path("C:\\Program Files\\Microsoft") # Or Path(r"C:\Program Files\Microsoft")# Mac Path("/usr/local/bin")Path() # Current Path("ecommerce/__init__.py…

AXI实战(一)-为AXI总线搭建简单的仿真测试环境

AXI实战(一)-搭建简单仿真环境 看完在本文后,你将可能拥有: 一个可以仿真AXI/AXI_Lite总线的完美主端(Master)或从端(Slave)一个使用SystemVerilog仿真模块的船信体验小何的AXI实战系列开更了,以下是初定的大纲安排: 欢迎感兴趣的朋友关注并支持,以下为正文部分 文章目录…

node.js笔记-模块化(commonJS规范),包与npm(Node Package Manager)

目录 模块化 node.js中模块的分类 模块的加载方式 模块作用域 向外共享模块作用域中的成员 向外共享成员 包与npm&#xff08;Node package Manager&#xff09; 什么是包&#xff1f; 包的来源 为什么需要包&#xff1f; 查找和下载包 npm下载和卸载包命令 配置np…

【数据结构】二叉排序树——平衡二叉树的调整

文章目录前置概念一、构造平衡二叉树的基本思想二、一个示例三、平衡二叉树的调整细节&#xff08;1&#xff09;LL型&#xff08;顺时针 &#xff09;举例&#xff08;2&#xff09;RR型&#xff08;逆时针&#xff09;&#xff08;3&#xff09;LR型&#xff08;先逆时针再顺…

测试左移之需求质量

测试左移的由来 缺陷的修复成本逐步升高 下面是质量领域司空见惯的一张图&#xff0c;看图说话&#xff0c;容易得出&#xff1a;大部分缺陷都是早期引入的&#xff0c;同时大部分缺陷都是中晚期发现的&#xff0c;而缺陷发现的越晚&#xff0c;其修复成本就越高。因此&#…

【Vue3 组件封装】vue3 轮播图组件封装

文章目录轮播图功能-获取数据轮播图-通用轮播图组件轮播图-数据渲染轮播图-逻辑封装轮播图功能-获取数据 目标: 基于pinia获取轮播图数据 核心代码&#xff1a; &#xff08;1&#xff09;在types/data.d.ts文件中定义轮播图数据的类型声明 // 所有接口的通用类型 export typ…

linux(centos7.6)docker

官方文档&#xff1a;https://docs.docker.com/engine/install/centos/1安装之前删除旧版本的docker2安装yum install-y yum-utils3配置yum源 不用官网的外国下载太慢 推荐阿里云yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.r…

笔记_js运算符

目录二进制相关运算符移位运算符<<>>&#xff5c;(位或运算)参考文档二进制相关运算符 移位运算符 移位运算就是对二进制进行有规律的移位。 tips:进制转换文档链接 << “<<”运算符执行左移位运算。在移位运算过程中&#xff0c;符号位始终保持不变…

jvm mat分析dump文件

jvm调优中&#xff0c;经常使用dump来分析是否存在大对象导致频繁full gc&#xff0c;以下为使用步骤&#xff1a;一、获得服务进程二、生成dump文件jmap -dump:formatb,filexxx.dump pid三、下载mat工具地址:https://www.eclipse.org/mat/downloads.php注意&#xff0c;12及以…

网络安全实验室5.上传关

5.上传关 1.请上传一张jpg格式的图片 url&#xff1a;http://lab1.xseclab.com/upload1_a4daf6890f1166fd88f386f098b182af/ 上传一张后缀名为jpg的图片&#xff0c;上传抓包修改后缀名为别的&#xff0c;s或者直接删掉&#xff0c;放包 得到key is IKHJL9786#$%^& 2.请…

再说多线程(六)——Thread生命周期

前面一直在用Thread介绍多线程任务&#xff0c;本节对线程类Thread的生命周期进行简单的梳理。线程状态对于一个线程来说&#xff0c;有以下几种状态&#xff1a;Unstarted(New) StateRunnable State(Ready to Run)Running StateNot Runable StateDead State这几种状态的转换关…

chatgpt国内能用的镜像与api请求样例

chatgpt去年刚出来时我就到openai注册了账号&#xff0c;必须用国外的线路才能注册&#xff0c;正常注册不了&#xff0c;注册完要用国外手机接收验证码&#xff0c;才能使用&#xff0c;我卡到验证码就没继续用了&#xff0c;昨晚&#xff0c;找了几个国内的镜像&#xff0c;用…

课程回顾|以智能之力,加速媒体生产全自动进程

本文内容整理自「智能媒体生产」系列课程第二讲&#xff1a;视频AI与智能生产制作&#xff0c;由阿里云智能视频云高级技术专家分享视频AI原理&#xff0c;AI辅助媒体生产&#xff0c;音视频智能化能力和底层原理&#xff0c;以及如何利用阿里云现有资源使用音视频AI能力。课程…

PyTorch学习笔记:nn.Sigmoid——Sigmoid激活函数

PyTorch学习笔记&#xff1a;nn.Sigmoid——Sigmoid激活函数 torch.nn.Sigmoid()功能&#xff1a;逐元素应用Sigmoid函数对数据进行激活&#xff0c;将元素归一化到区间(0,1)内 函数方程&#xff1a; Sigmoid(x)σ(x)11e−xSigmoid(x)\sigma(x)\frac1{1e^{-x}} Sigmoid(x)σ(…