sqlparse
sqlparse用于拆分、解析和格式化 SQL 语句的 Python 库。
公开接口
他提供了三个公开的接口来实现拆分、解析和格式化:
sqlparse.split(sql: str, encoding=None) -> list[str]
: 将多条 SQL 语句拆分为单条语句sqlparse.format(sql:str, encoding=None, **options) -> str
: 根据选项格式化 SQLsqlparse.parse(sql:str, encoding=None) -> tuple(Statement)
: 解析 SQL 并返回一个元组其中包含表示语句的 Statement 对象
split
该方法是最简单的:
sql = "SELECT * FROM users; INSERT INTO users (id, name) VALUES (1, 'Alice');"
statements = sqlparse.split(sql)
for stmt in statements:
print(stmt)
# SELECT * FROM users;
# INSERT INTO users (id, name) VALUES (1, 'Alice');
format
他用于格式化 SQL 语句,他接受以下参数:
keyword_case: "upper" | "lower" | "capitalize"
: 关键字格式,默认就是关键字全部大写identifier_case: "upper" | "lower" | "capitalize"
: 标识符格式strip_comments: bool = False
: 如果为 True 这删除注释truncate_strings: number|None = None
: 如果是正整数,长度超过给定值的字符串被截断truncate_char: str = "[...]"
: 被截断的字符串后附加的字符串reindent_aligned: bool = False
: 如果为 True 将缩进语句并通过关键字对齐output_format: "python" | "php"
: 输出为对应语言的字符串变量(主要是添加一些转义)
这其中比较有用的就是格式化打印以及输出 Python 来让 ORM 使用:
sqlparse.format(sql, reindent_aligned=True, output_format='python')
# 输出是字符串
# "sql = ('SELECT * '\n ' FROM users;')\nsql2 = ('INSERT INTO users (id, name) VALUES (1, \\'Alice\\');')"
parse
这个 sqlparse 库最核心的接口,从他的库名称也能看出来。他使用起来非常简单:
sql = "SELECT * FROM users; INSERT INTO users (id, name) VALUES (1, 'Alice');"
statements = sqlparse.parse(sql)
#(<Statement 'SELECT...' at 0x7F78951C08A0>, <Statement 'INSERT...' at 0x7F7895015160>)
他首先会将 SQL 语句分解,然后每条语句包装成一个 sqlparse.sql.Statement
对象,他表示一条完整的 SQL 语句的分析树,树上的每个叶子使用 Token 表示枝干使用 TokenList 表示。而继承自 TokenList 的其他对象表示 SQL 语句的各个部分,整个 sqlparse 库的对象的继承关系如下:
sqlparse.sql.Token
- sqlparse.sql.TokenList
- sqlparse.sql.Statement
- sqlparse.sql.Identifier
- sqlparse.sql.IdentifierList
- sqlparse.sql.TypedLiteral
- sqlparse.sql.Parenthesis
- sqlparse.sql.SquareBrackets
- sqlparse.sql.Assignment
- sqlparse.sql.If
- sqlparse.sql.For
- sqlparse.sql.Comparison
- sqlparse.sql.Comment
- sqlparse.sql.Where
- sqlparse.sql.Having
- sqlparse.sql.Case
- sqlparse.sql.Function
- sqlparse.sql.Begin
- sqlparse.sql.Operation
- sqlparse.sql.Values
- sqlparse.sql.Command
其中 Token 表示 SQL 语句的最小组成部分例如关键字、标识符、空白、运算符等等。而 TokenList 表示一组 Token 的集合。而更加具体的 Statement 表示 SQL 语句,Identifier 表示标识符等。因此所有的操作都绑定在 Token 和 TokenList 上。
Token
Token 表示 SQL 语句的最小组成部分:
这其中最为特殊的就是 ttype,需要注意 TokenList 本身也继承自 Token,但是 TokenList.ttype 为 None,并且继承在其上的所有对象都为 None 。因此要记住ttype 只是针对于 SQL 语句的最小组成部分的 Token 而言的。
Tips
TokenList.tokens
能够返回组成 TokenList 的所有 Token 这也是 TokenList 最常使用的接口。
sqlparse.tokens 的层次结构
所谓的层次结构就是叶的名称,他们对应的就是 Token.ttype:
Text
: 文本相关的类型hitespace
: 空白字符,如空格、制表符Whitespace.Newline
: 换行符Comment
: 注释Comment.Single
: 单行注释Comment.Multiline
: 多行注释
Keyword
: 关键字Keyword
: 一般关键字(例如 SELECT, FROM, INSERT 等)Keyword.DML
: 数据操作语言 (DML) 关键字(例如 SELECT, INSERT, UPDATE, DELETE)Keyword.DDL
: 数据定义语言 (DDL) 关键字(例如 CREATE, DROP, ALTER)Keyword.TCL
: 事务控制语言 (TCL) 关键字(例如 COMMIT, ROLLBACK)Keyword.CTE
: 公共表表达式 (WITH) 关键字Keyword.Reserved
: 保留字关键字Keyword.Type
: 数据类型关键字(例如 VARCHAR, INT)
Name
: 名称、标识符Name
: 一般的名称或标识符Name.Builtin
: 内置名称(例如函数名,如 COUNT)Name.Constant
: 常量名称Name.Variable
: 变量Name.Function
: 函数名称Name.Class
: 类名(一般用于数据库对象的类,如表名、视图名)Name.Namespace
: 命名空间Name.Label
: 标签Name.Entity
: 实体(用于对象或表)Name.Builtin.Pseudo
: 伪列(例如 Oracle 中的 ROWNUM)
Literal
: 字面值Literal
: 一般的字面值Literal.String
: 字符串字面值Literal.String.Single
: 单引号字符串Literal.String.Symbol
: 符号(例如运算符)Literal.String.Heredoc
: 多行字符串(用于某些 SQL 方言)
Literal.Number
: 数字字面值Literal.Number.Integer
: 整数Literal.Number.Float
: 浮点数Literal.Number.Hexadecimal
: 十六进制数
Operator
: 运算符Operator
: 一般运算符(例如 +, -, *, /)Operator.Comparison
: 比较运算符(例如 =, <>, <=, >=)
Punctuation
: 标点符号Punctuation
: SQL 语句中的标点符号(例如逗号、括号)
Wildcard
: 通配符Wildcard
: 通配符(例如 *)
Comparison
: 比较符号Comparison
: 特定的比较符号(例如 =, !=)
Error
: 错误Error
: 标记为错误的令牌,通常是由于无法解析的 SQL 语句
Other
: 其他类型 -Other
: 无法归类到其他类别的 token
一个使用场景就是如果我们想要获取所有的字面量(主要是 Value 子句中的),可以使用:
# 所有的 tokens 都是 tuple 像 Other 里面只有一个 Other 而 Literal 就包含很多其他 ttype
from sqlparse.tokens import Literal
for token in token_list:
if token.ttype in Literal:
print(token.value)
Tips
由于 TokenList.tokens 返回的结果包含 Token 和 TokenList。需要注意 ttype 和 TokenList 子类的区别
TokenList
他表示一个树节点,其中最核心的就是 tokens 属性来返回其包含的所有 Token 对象:
返回的 Token 如果不是最小的节点,那么可能依然是 TokenList,我们需要继续下分。看一下的示例:
import sqlparse
sql_str = "INSERT INTO users (id, name, bio) VALUES (1, 'Alice', 'Hello, I\\'m a developer.');"
# parse 一定返回 tuple 因为上面只有一条语句因此需要 0 来提取该语句
stem = sqlparse.parse(sql_str)[0]
# 结果是 Statement 对象,他是 TokenList 的子类
<Statement 'INSERT...' at 0x7F78952840C0>
# 最常用的接口就是通过 TokenList.tokens 来获取所有的 Token
stem.tokens
# 结果是一个列表,其中包含了所有的 Token
[<DML 'INSERT' at 0x7F789525C280>,
<Whitespace ' ' at 0x7F7895049540>,
<Keyword 'INTO' at 0x7F7894FED120>,
<Whitespace ' ' at 0x7F7894FEE080>,
<Function 'users ...' at 0x7F7895284A60>,
<Whitespace ' ' at 0x7F7894FED360>,
<Values 'VALUES...' at 0x7F78952846E0>,
<Punctuation ';' at 0x7F7894FEFAC0>]
# 上面的 DML、Whitespace、keyword 等都是 Token 这些名字对应了他们的 Token.ttype
# 而 Function 和 Values 都是 TokenList 对象他们可以继续下分
stem.tokens[6].tokens
[<Keyword 'VALUES' at 0x7F7895069900>,
<Whitespace ' ' at 0x7F7895069960>,
<Parenthesis '(1, 'A...' at 0x7F7895096270>]
# 可以看到返回的依然包含 Parenthesis 这个 TokenList 对象
Tips
这个库比较容易混淆的就是 ttype 和 TokenList 子类,他们的打印结果比较雷同