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)

大家都在看

  • 学习笔记——Django项目中关联查询以及关联查询的筛选

    2022-10-01 关联查询: 在Django项目中使用ORM模式设置表后,进行关联查询,即两个表直接有联系的查询。 方式: 可以通过主表查询从表,也可以通过从表查询主表。 方式…

    数据库 2023年6月14日
    084
  • Java中如何遍历字符串呢?

    字符串是程序开发中我们见的最多的一种数据类型 对字符串的操作,也是我们日常涉及的最多的一种操作方式,那么如何遍历字符串为字符并输出呢? 下面笔者讲述三种操作方式,如下所示 1.直接…

    数据库 2023年6月11日
    066
  • 在CentOS 7系统安装StoneDB数据库

    今天我会进行StoneDB数据库在CentOS 7系统下的安装。 在官方的快速部署文档中有详细的安装流程,我会严格遵循流程。 [En] There is a detailed in…

    数据库 2023年5月24日
    0109
  • Handler_read_*的总结

    在分析一个SQL的性能好坏时,除了执行计划,另外一个常看的指标是”Handler_read_*”相关变量。 Handler_read_key Handler…

    数据库 2023年6月11日
    093
  • Java百度地图经纬度纠偏

    在国内使用电子地图获取到的经纬度都不是真实的经纬度,而是经过一定的算法在真实的经纬度上添加了一个偏移量,且不同的地图有不同的算法。现在告诉大家在java中怎样对百度地图进行纠偏,主…

    数据库 2023年6月9日
    094
  • vue进阶(一)

    vue.js 数据绑定 定义:将数据和视图相关联,当数据发生变化时,可以自动更新视图。 语法 1) 插值 使用双大括号{{}},双大括号会将里面的值当做字符串进行处理;而如果值是H…

    数据库 2023年6月6日
    0116
  • 如何基于LSM-tree架构实现一写多读

    PolarDB是阿里巴巴自研的新一代云原生关系型数据库,在存储计算分离架构下,利用了软硬件结合的优势,为用户提供具备极致弹性、海量存储、高性能、低成本的数据库服务。X-Engine…

    数据库 2023年5月24日
    089
  • 多商户商城系统功能拆解27讲-平台端分销结算设置

    多商户商城系统,也称为B2B2C(BBC)平台电商模式多商家商城系统。可以快速帮助企业搭建类似拼多多/京东/天猫/淘宝的综合商城。 多商户商城系统支持商家入驻加盟,同时满足平台自营…

    数据库 2023年6月14日
    097
  • 2_JDBC

    使用客户端工具访问数据库, 需要手工建立连接, 输入用户名和密码登陆, 编写SQL语句, 点击执行, 查看操作结果(结果集或受行数影响) 在实际开发中, 当用户的数据发生改变时, …

    数据库 2023年6月11日
    072
  • 内部类

    🐓内部类 可以将一个类定义在另一个类或方法中,这样的类称为内部类 将类定义在另一个类中成员的位置 public class Inner { // 定义在类内部 class Demo…

    数据库 2023年6月14日
    082
  • 博客园美化-随季节变化实现不同的飘落效果

    最近在研究博客园的美化效果,看到有一个樱花飘落的效果,忽然突发奇想,如果能根据当前日期所处的季节实现不同的飘落效果岂不是更酷。😂 最近在研究博客园的美化效果,看到有一个樱花飘落的效…

    数据库 2023年6月6日
    0127
  • eclipse连接MySQL 8.0.29.0

    推荐文章: eclipse导入JDBC MySQL详细安装 菜鸟java MySQL连接教程 步骤: 找到MySQL的连接Java的jar文件; 如下图: 在eclipse项目文件…

    数据库 2023年5月24日
    0123
  • mysql语法使用详细代码版

    mysql 1.什么是数据库 数据库:(DB,DataBase)作用:用来存储数据,管理数据。Txt,Excel,word是在数据库出现之前存储数据的。概念:数据仓库,安装在操作系…

    数据库 2023年5月24日
    091
  • 在OAuth 2.0模式下使用Spring Cloud Gateway

    Spring Cloud Gateway主要用于以下角色之一: OAuth Client *OAuth Resource Server 1 Spring Cloud Gateway…

    数据库 2023年6月14日
    0118
  • 记一次MySql唯一索引在left join连表查询没走索引的问题

    在新建一张账单结算信息表bill_settlement_info的时候,建立的唯一索引uk_bill_no(bill_no,tenant_id)。由于列表查询用到该表的字段。所以在…

    数据库 2023年6月16日
    091
  • Navicat 连接服务器不成功(Access denied for user ‘root’@ ‘*.*.*.*’ (using password: YES))

    出现的原因一般是服务器的root用户没有开启访问权限,一般来说值允许本地的访问。 解决方法: 一:第一种方法 1、首先打开xshell连接服务器的终端 2、以root权限登录 my…

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