Python-sqlparse解析SQL工具库一文详解(一)

目录

前言

一、sqlparse简介

二、功能代码解析

1.初始方法

1.parse

 2.parsestream

3.format

4.split

2.基类-Token

1.flatten()

 2.match(ttype, values, regex=False)

点关注,防走丢,如有纰漏之处,请留言指教,非常感谢


前言

写此sqlparse库的目的还是寻找在python编程内可行的SQL血缘解析,JAVA去解析Hive的源码实践的话我还是打算放到后期来做,先把Python能够实现的先实现完。上篇系列讲述的基于antrl解析说是用python其实还是太牵强了,无非就是使用PyJnius调用JAVA的类方法来实现,没有多大的意义来牵扯到Python编程。主要是HiveSQL的底层就是JAVA代码,怎么改写还是绕不开JAVA的。不过上篇系列我有提到过sqlparse,其实这个库用来解析血缘的话也不是不可以,但是能够实现的功能是有限的,目前我实验还行,一些复杂超过千行的数据分析SQL没有测试过。做一些简单的血缘解析的话还是没有应该太大问题,后续我会在此基础之上开发尝试。


一、sqlparse简介

首先先给官网地址:python-sqlparse。有足够好编码能力可以直接上github上面看源码,解读更细:github.sqlparse

sqlparse是用于Python的非验证SQL解析器。它支持解析、拆分和格式化SQL语句。既然有解析功能那么我们就能做初步的血缘解析功能。这个库的函数解析没有像Pandas和numpy写的那么详细,毕竟是人家个人的开源库,功能写的已经很不错了,能够省去我们很多递归剥离AST树的时间。官网上关于该库使用操作很简单,很多比较好的功能函数也没有使用到,我希望可以尽力将此库开发为通用SQL血缘解析的基础工具库。如果该功能开发完我会将此项目开源。

我通过细读源码来了解此库的大体功能。

二、功能代码解析

1.初始方法

看初始化代码方法有四种:parse,parsestream,format,split这四种

1.parse

def parse(sql, encoding=None):
    """Parse sql and return a list of statements.
    :param sql: A string containing one or more SQL statements.
    :param encoding: The encoding of the statement (optional).
    :returns: A tuple of :class:`~sqlparse.sql.Statement` instances.
    """
    return tuple(parsestream(sql, encoding))

传入一个SQL语句,返回一个 sqlparse.sql.Statement的元组,我们可以递归方式获得输出。

query = 'Select a, col_2 as b from Table_A;'
for each in sqlparse.parse(query):
    print(each)

其元组根据;符号来进行切分存储:

query = 'Select a, col_2 as b from Table_A;select * from foo'
for each in sqlparse.parse(query):
    print(each)

 

 2.parsestream

可以看到第一个方法是调用了parsestream来完成流式解析的,那么这个方法也就是循环读取sql语句来完成转换statment的:

def parsestream(stream, encoding=None):
    """Parses sql statements from file-like object.
    :param stream: A file-like object.
    :param encoding: The encoding of the stream contents (optional).
    :returns: A generator of :class:`~sqlparse.sql.Statement` instances.
    """
    stack = engine.FilterStack()
    stack.enable_grouping()
    return stack.run(stream, encoding)

这里的引擎是可以替换的。

sqlparse.parsestream(query)

它将返回一个sqlparse.sql.Statement实例的发生器。来看看这个run方法:

def run(self, sql, encoding=None):
        stream = lexer.tokenize(sql, encoding)
        # Process token stream
        for filter_ in self.preprocess:
            stream = filter_.process(stream)

        stream = StatementSplitter().process(stream)

        # Output: Stream processed Statements
        for stmt in stream:
            if self._grouping:
                stmt = grouping.group(stmt)

            for filter_ in self.stmtprocess:
                filter_.process(stmt)

            for filter_ in self.postprocess:
                stmt = filter_.process(stmt)

            yield stmt

 该方法就是生产一个statment,这个类应该就是这个库的基类了,多半围绕这个数据结构来处理。

3.format

该方法就是将sql语句标准化:

query = 'Select a, col_2 as b from Table_A;select * from foo'
print(sqlparse.format(query, reindent=True, keyword_case='upper'))

 

format()函数接受关键字参数:

  •     keyword_case 关键词upper、lowersql的保留字大小写
  •     identifier_case 标识符的upper、lower大小写
  •     strip_comments=Ture删除注释
  •     reindent=Ture美化sq缩进语句发生改变

4.split

该方法用于分割sql语句:

sqlparse.split(query)

这里补充一下calss类sqlparse.sql.Statement是可以直接通过str转换为字符串的。

结果返回一个分割后的list。至此初始方法就写完了,下面我将详解一下基类,这将决定是我们是否能灵活运用此库。

2.基类-Token

我们来看看Token的初始方法属性:

def __init__(self, ttype, value):
        value = str(value)
        self.value = value
        self.ttype = ttype
        self.parent = None
        self.is_group = False
        self.is_keyword = ttype in T.Keyword
        self.is_whitespace = self.ttype in T.Whitespace
        self.normalized = value.upper() if self.is_keyword else value

 这个Token类也就是语法解析器的重点数据流了:

 此类需要生成Tokens使用,这牵扯到另一个方法tokens.py

此方法也就是将statment类转换为Token流:

parsed = sqlparse.parse(query)
stmt = parsed[0]
stmt.tokens

 

 其中我们需要解析的每个Token的标识码也就是第一个ttype属性,解析之后:

for each_token in sql_tokens:
    print(each_token.ttype,each_token.value)

 

 我们拿一个Token来研究就能逐渐解析到其他token。我们建立一个列表将其主要属性ttype和value收集起来:

type(list_ttype[0])
type(list_value[0])

 第一个属性为sqlparse.tokens._TokenType第二个value直接就是str了。上tokens看_TokenType:

# Special token types
Text = Token.Text
Whitespace = Text.Whitespace
Newline = Whitespace.Newline
Error = Token.Error
# Text that doesn't belong to this lexer (e.g. HTML in PHP)
Other = Token.Other

# Common token types for source code
Keyword = Token.Keyword
Name = Token.Name
Literal = Token.Literal
String = Literal.String
Number = Literal.Number
Punctuation = Token.Punctuation
Operator = Token.Operator
Comparison = Operator.Comparison
Wildcard = Token.Wildcard
Comment = Token.Comment
Assignment = Token.Assignment

# Generic types for non-source code
Generic = Token.Generic
Command = Generic.Command

# String and some others are not direct children of Token.
# alias them:
Token.Token = Token
Token.String = String
Token.Number = Number

# SQL specific tokens
DML = Keyword.DML
DDL = Keyword.DDL
CTE = Keyword.CTE

 可以发现这就是Token的识别解析类型码,通过该码就可以访问获得解析出的关键字了。

关于此基类又有五种主要的方法:

1.flatten()

用于解析子组

for each_token in sql_tokens:
    #list_ttype.append(each_token.ttype),list_value.append(each_token.value)
    print(each_token.flatten())

 2.match(ttype, values, regex=False)

检查标记是否与给定参数匹配。

list_ttype=[]
list_value=[]
for each_token in sql_tokens:
    #list_ttype.append(each_token.ttype),list_value.append(each_token.value)
    print(each_token.match(each_token.ttype,each_token.ttype))

 

 or运算为None匹配为True输出。

ttype是一种token类型。如果此标记与给定的标记类型不匹配。values是此标记的可能值列表。这些values一起进行OR运算,因此如果只有一个值与True匹配,则返回。除关键字标记外,比较区分大小写。为了方便起见,可以传入单个字符串。如果regex为True(默认值为False),则给定值将被视为正则表达式。

另外还有三种方法has_ancestor(other),is_child_of(other),within(group_cls)这都有调用功能函数相关,可以先不用了解。

由此Token传入流单体已经差不多分析完,但是AST树该如何生成这是个问题,还有关于树的递归问题和层级问题,我们继续根据基类来慢慢摸清。这篇文章已经足够多内容了,先打住。下一篇再细讲。

点关注,防走丢,如有纰漏之处,请留言指教,非常感谢

以上就是本期全部内容。我是fanstuck ,有问题大家随时留言讨论 ,我们下期见


### 配置 VSCode 中的 SSM 开发环境 #### 、安装必要的扩展 为了更好地支持 Java 和 Maven 项目开发,需在 Visual Studio Code (VSCode) 上安装些有用的插件。 - **Java Extension Pack**: 这个包包含了编写和运行 Java 所必需的各种工具和支持[^1]。 - **Maven for Java**: 支持通过图形界面管理依赖项以及构建过程[^2]。 ```bash ext install vscjava.vscode-java-pack ext install vscjava.vscode-maven ``` #### 二、创建并导入 Maven 工程 使用命令行或者集成终端来创建个新的 Maven 项目结构,并将其作为工作区打开于编辑器内。可以利用 `archetype` 插件快速搭建基础架构: ```xml <groupId>com.example</groupId> <artifactId>ssm-demo</artifactId> <version>1.0-SNAPSHOT</version> <!-- 添加 spring, mybatis 及其他所需库 --> <dependencies> <!-- Spring Framework Dependencies --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> <!-- MyBatis Dependency --> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>${mybatis.version}</version> </dependency> <!-- 数据源配置 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>${druid.version}</version> </dependency> </dependencies> ``` #### 三、配置数据源和其他属性文件 按照惯例,在项目的 resources 文件夹下放置 application.properties 或者 database.properties 来定义数据库连接字符串以及其他全局参数设置[^3]。 ```properties jdbc.driver=com.mysql.cj.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/ssm?useSSL=false&serverTimezone=UTC jdbc.username=root jdbc.password=password ``` #### 四、调整 Web 应用程序上下文加载机制 为了让应用程序能够正确识别静态资源路径并且初始化 Spring IoC 容器,应在 web.xml 中注册 `ContextLoaderListener` 监听器。 ```xml <context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/applicationContext.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> ``` #### 五、测试与调试 完成上述步骤之后就可以尝试编译整个工程并通过内置服务器部署应用来进行初步的功能验证了;如果遇到任何问题可以通过断点调试的方式逐步排查错误原因。
评论 60
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

fanstuck

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值