Pisa-Proxy SQL 解析之 Lex & Yacc

一、前言

1.1 作者介绍

王波,SphereEx MeshLab 研发工程师,目前专注于 Database Mesh,Cloud Native 的研发。Linux,llvm,yacc,ebpf user。 Gopher & Rustacean and c bug hunter。

GitHub: https://github.com/wbtlb

1.2 背景

在上篇文章《Pisa-Proxy 之 SQL 解析实践》中介绍了 Pisa-Proxy 的核心模块之一 SQL 解析器的相关内容。在 MySQL 和 PostgreSQL 中 SQL 解析是通过 Yacc 实现的,同样 Pisa- Proxy 的 SQL 解析器是由类似 Yacc 这样的工具实现的,所以本篇文章会围绕 SQL 解析器为大家介绍一些编译原理和 Lex & Yacc 的使用,同时也会为读者展示如何通过 Lex & Yacc 实现一个简单的 SQL 解析器。从而帮助大家更好地理解 Pisa-Proxy 中 SQL 解析器是如何工作的。

二、编译器初探

一个程序语言不论是我们常用的 Java,Golang 或者是 SQL 本质上都是一个记号系统,如同自然语言一样,它的完整定义应该包括语法和语义两个方面。一种语言的语法其实是对应的一组规则,用它可以形成和产生一个合适的程序。当前使用最广泛的手段是上下文无关的文法,上下文无关的文法作为程序设计语言语法的描述工具。语法只是定义什么样的符号序列是合法的,与这些符号的含义毫无关系。然而在语义中分为两类:静态语义和动态语义。静态语义是指一系列的限定规则,并确定哪些语法对于程序来说是合适的;动态语义也称作运行语义或者执行语义,明确程序具体要计算什么。

2.1 编译器工作流程

如图 2.1.1 中所示,通常编译器将源代码编译成可执行文件主要有以下几步:

  1. 对源文件进行扫描,将源文件的字符流拆分分一个个的词(token),此为词法分析
  2. 根据语法规则将这些记号构造出语法树,此为语法分析
  3. 对语法树的各个节点之间的关系进行检查,检查语义规则是否被违背,同时对语法树进行必要的优化,此为语义分析
  4. 遍历语法树的节点,将各节点转化为中间代码,并按特定的顺序拼装起来,此为中间代码生成
  5. 对中间代码进行优化
  6. 将中间代码转化为目标代码
  7. 对目标代码进行优化,生成最终的目标程序

Pisa-Proxy SQL 解析之 Lex & Yacc

对于 SQL 解析来说,就可以将上图中的步骤简化为如图 2.1.2 的形式,源码输入(SQL 语句),将 SQL 语句进行词法分析,生成 SQL 中特定的 token 记号流。然后拿到记号流后进行语法分析后生成最终的 SQL AST。

Pisa-Proxy SQL 解析之 Lex & Yacc

2.2 词法分析

上文中提到,无论是编译器还是 SQL 解析器有一个关键步骤就是要对源文件做词法分析,词法分析我们可以理解为对 SQL 语句本身做分词处理。那么在这个阶段,SQL 解析器要做的工作就是从左到右扫描源文件,将 SQL 语句分割成一个个的 token,这里说的 token 是指 SQL 中不能再进一步分割的一串字符。例如图 2.1.2 中的 SQL 语句,经过词法分析后,生成的 token 为: SELECT*FROMpisa_proxy 等等。

在 SQL 语句中能用到的 token 类别也是有限的,比如保留字 SELECTINSERTDELETE 等等。还有操作符,比如:算术操作符、比较操作符。还有标识符,比如:内置函数名等等。在此阶段每扫描一个 token 会被维护到一个数据结构中,然后在下个阶段语法分析阶段使用。
通常来说,词法分析有直接扫描,正则匹配扫描方式。

2.2.1 直接扫描法

直接扫描法逻辑非常清晰,每次扫描根据第一个字符判断属于哪种类型的 token,然后采取不同的策略扫描出一个完整的 token,然后再进行下一轮扫描。在 Pisa-Proxy 中的 SQL 解析中,词法分析就采用了这种实现方式,用 Python 展示如何实现一个简单的 SQL 词法分析器对 SQL 进行扫描,代码如下:

-*- coding: utf-8 -*-

single_char_operators_typeA = {
    ";", ",", "(", ")","/", "+", "-", "*", "%", ".",
}

single_char_operators_typeB = {
    "<", ">", "=", "!"
}

double_char_operators = {
    ">=", "<=", 0 1 34 50 "="="," "~="
}

reservedWords = {
    " select", "insert", "update", "delete", "show", "create", "set", "grant", "from", "where" } class token: def __init__(self, _type, _val="None):" if is none: self.type="T_" + _type; self.val="_type;" else: self.type, __str__(self): return "%-20s%s" % (self.type, self.val) noneterminatequoteerror(exception): pass iswhitespace(ch): ch in \t\r\a\n" isdigit(ch): "0123456789" isletter(ch): "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz" scan(s): n, i="len(s)," while < n: ch, continue "#": single_char_operators_typea: yield token(ch) elif single_char_operators_typeb: n and s[i]="=" =") isletter(ch) or "_": begin="i" - (isletter(s[i]) isdigit(s[i]) "_"): word="s[begin:i]" reservedwords: token(word) token("t_identifier", word) adot="False" ".": adot: raise exception("too many dot a number!\n\tline:"+line) not isdigit(s[i]): break token("t_double" else "t_integer", s[begin:i]) ord(ch)="=" 34: # means '"' ord(s[i]) !="34:" exception("non-terminated string quote!\n\tline:"+line) token("t_string", chr(34) s[begin:i] chr(34)) exception("unknown symbol!\n\tline:"+line+"\n\tchar:"+ch) __name__="=" "__main__": print ("token type", "token value") "-" * sql="select * from pisa_proxy where id = 1;" for token scan(sql): code></=",></",>

最终的输出结果如下:

`
TOKEN TYPE TOKEN VALUE

Original: https://www.cnblogs.com/sphereex/p/16454723.html
Author: SphereEx
Title: Pisa-Proxy SQL 解析之 Lex & Yacc

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/621224/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • Consul 入门-运行

    HashiCorp Consul 是由 HashiCorp 公司开发的,它是一家专注于 DevOps 工具链的公司,旗下的明星级产品包括 Vagrant、Terraform、Vau…

    数据库 2023年6月6日
    076
  • 删除chrome的域名安全策略

    使用过程中总有特殊情况必须使用http请求、又或者必须使用https请求 目前有两种思路,一种是浏览器默认开了http转https请求,像chrome和Firefox,另一种就是服…

    数据库 2023年6月6日
    096
  • feign之间传递oauth2-token的问题和解决~续

    之前写过关于修改hystric的隔离《feign之间传递oauth2-token的问题和解决》方式来在feign调用各个微服务中传递token,修改为SEMAPHORE之后,会有一…

    数据库 2023年6月6日
    086
  • Spring Boot中异步请求和异步调用

    一、SpringBoot中异步请求的使用 1、异步请求与同步请求 特点: 可以先释放容器分配给请求的线程与相关资源,减轻系统负担,释放了容器所分配线程的请求,其响应将被延后,可以在…

    数据库 2023年6月14日
    088
  • 基础算法知识

    一、冒泡排序 冒泡排序其实跟握手定理差不多(即A,B,C三人需每两个都都要握手一次 AB,AC,BC) 时间复杂度比较差的O(n²) int[] arrays = {2, 1, 5…

    数据库 2023年6月6日
    0116
  • MySQL触发器

    触发器 先来个实际的案例 触发器概述 和存储过程一样,都是嵌入在MySQL服务器中的一段程序 触发器由 事件触发,比如INSERT ,UPDATE 等用户的动作或触发某项行为,自动…

    数据库 2023年5月24日
    096
  • 手写spring的ioc的流程截图(笔记-1)

    spring ioc是什么? IoC 容器是 Spring 的核心,也可以称为 Spring 容器。Spring 通过 IoC 容器来管理对象的实例化和初始化,以及对象从创建到销毁…

    数据库 2023年6月6日
    072
  • Github 星标 8K+ 这款国人开源的 Redis 可视化管理工具,真香…

    做程序员就少不了与一些工具打交道,比如:监控工具、管理工具等,有些工具是命令行界面,有些工具是可视化界面,反正都是可以能够满足日常使用的功能需求。 对于redis管理工具来说,也有…

    数据库 2023年6月9日
    0112
  • Postman安装及汉化

    1. 安装postman 各位根据各自需要可以参考下表自行下载,把链接内的”版本号”替换为指定的版本号 操作系统 下载链接 Windows64位 Windo…

    数据库 2023年6月14日
    096
  • MySQL 关于 only_full_group_by 限制

    先上结论 如果 only_full_group_by 被启用,那么在查询时,如果某个列不在group by 列表中,此时如果不对该列进行聚合处理,则该列不能出现在 select 列…

    数据库 2023年6月16日
    099
  • MySQL实战45讲 3

    03 | 事务隔离:为什么你改了我还看不见? 事务 Transaction TRX 事务就是 要保证一组数据库操作,要么全部成功,要么全部失败。 MySQL 原生的 MyISAM …

    数据库 2023年6月16日
    090
  • 使用mybatis连接数据库–针对小白

    实现mybatis连接数据库的步骤: 1.建表 2.pom.xml的配置 <?xml version="1.0" encoding="UTF-8…

    数据库 2023年6月11日
    059
  • Linux中MySQL的安装以及卸载

    一.MySQL MySQL 是一种开放源代码的关系型数据库管理系统,开发者为瑞典MySQL AB公司。在2008年1月16号被Sun公司收购。而2009年,SUN又被Oracle收…

    数据库 2023年5月24日
    0102
  • 解决 IDEA 2021.2.3 新建maven项目只有两个archetype项目模板的问题

    最近把我的 IDEA 版本更新到 2021.2.3 了,发生了一个比较有意思的问题,做个小小的记录 思路分析 在新的 IDEA 中配置完Maven之后,想要创建Maven项目的时候…

    数据库 2023年6月11日
    081
  • 学会Linux,看完这篇就行了!

    转载请注明出处❤️ 作者:测试蔡坨坨 原文链接:caituotuo.top/797ab07d.html 你好,我是测试蔡坨坨。 对于测试同学来说,Linux基本属于必学必会内容,招…

    数据库 2023年6月11日
    077
  • 2022-08-16 数据库查询语言之——-DQL

    重点,DQL是我们每天都要接触编写最多也是最难的SQL,该语言用来查询记录,不会修改数据库和表结构。 构建数据库 创建一张student表: DROP TABLE IF EXIST…

    数据库 2023年6月14日
    086
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球