该项目确实有点缺乏记录。我看了看examples https://github.com/andialbrecht/sqlparse/tree/master/examples并稍微扫描了一下源代码。不幸的是,该文档并未包含所有方法Token
and TokenList
对于此任务有用的类。
例如,一个重要但被忽略的方法是TokenList.get_sublists() method https://github.com/andialbrecht/sqlparse/blob/47768597924d88cec426e379b4afae8b58491956/sqlparse/sql.py#L201-L204,它可以让您比其他方法更轻松地遍历嵌套标记列表;这TokenList.flatten() https://sqlparse.readthedocs.io/en/latest/analyzing/#sqlparse.sql.TokenList.flatten方法仅产生未分组的树中的标记,而CASE
是一个分组令牌,因此纯粹根据文档,您可能会发现很难使用解析的令牌树做一些有用的事情。
我在代码库中注意到的另一个方便的方法是TokenList._pprint_tree() method https://github.com/andialbrecht/sqlparse/blob/master/sqlparse/sql.py#L162-L178,它将当前令牌树转储到标准输出。当尝试编写分析树的代码时,这非常有帮助。
总而言之我的总体印象是sqlparse
与其说它是一个解析库,不如说它是一个重新格式化 SQL 的工具。它包括一个很好的解析器,但不包括制作所需的工具一般使用它所产生的树。
图书馆真正缺少的是一个基地节点访问者类例如由Python ast module https://docs.python.org/3/library/ast.html#ast.NodeVisitor,或者树节点遍历器,又像ast模块提供 https://docs.python.org/3/library/ast.html#ast.walk。幸运的是,这两种方法都很容易自己构建:
from collections import deque
from sqlparse.sql import TokenList
class SQLTokenVisitor:
def visit(self, token):
"""Visit a token."""
method = 'visit_' + type(token).__name__
visitor = getattr(self, method, self.generic_visit)
return visitor(token)
def generic_visit(self, token):
"""Called if no explicit visitor function exists for a node."""
if not isinstance(token, TokenList):
return
for tok in token:
self.visit(tok)
def walk_tokens(token):
queue = deque([token])
while queue:
token = queue.popleft()
if isinstance(token, TokenList):
queue.extend(token)
yield token
现在您可以使用其中之一来访问Case
nodes:
statement, = sqlparse.parse(query)
class CaseVisitor(SQLTokenVisitor):
"""Build a list of SQL Case nodes
The .cases list is a list of (condition, value) tuples per CASE statement
"""
def __init__(self):
self.cases = []
def visit_Case(self, token):
branches = []
for when, then_ in token.get_cases():
branches
self.cases.append(token.get_cases())
visitor = CaseVisitor()
visitor.visit(statement)
cases = visitor.cases
or
statement, = sqlparse.parse(query)
cases = []
for token in walk_tokens(statement):
if isinstance(token, sqlparse.sql.Case):
cases.append(token.get_cases())
之间的区别walk_tokens()
and NodeVisitor
在此示例中,模式可以忽略不计,但我们只是简单地为每个模式提取单独的标记CASE
语句,不处理WHEN ... THEN ...
代币。在里面NodeVisitor
模式,您可以在当前访问者实例上设置更多属性来“切换齿轮”,并在更多中捕获有关这些子树标记的更多信息visit_....
方法,这可能比嵌套更容易遵循for
循环生成器。
另一方面,随着walk_tokens()
生成器,如果您创建一个单独的变量来引用生成器,则可以将迭代移交给辅助函数:
all_tokens = walk_tokens(stamement)
for token in walk_tokens(statement):
if isinstance(token, sqlparse.sql.Case):
branches = extract_branches(all_tokens)
where extract_branches
将进一步迭代,直到到达 case 语句的末尾。