这个项目确实有点缺乏文件记录。我查看了examples并扫描了一下源代码。不幸的是,文档中并没有包括Token和TokenList类上对该任务有用的所有方法。在
例如,一个重要但被忽略的方法是^{} method,它使您比其他方法更容易遍历嵌套的标记列表;^{}方法只在树中生成未分组的标记,而CASE是一个分组标记,因此,单纯地看文档,您可能会发现很难对解析的令牌树做一些有用的事情。在
我在代码库中注意到的另一个方便的方法是^{} method,它将当前的令牌树转储到stdout。这在试图编写分析树的代码时非常有用。在
总之,我对sqlparse的总体印象是,它与其说是一个解析库,不如说是一个重新格式化SQL的工具。它包含了一个很好的解析器,但不包括使通用于其生成的树的必要工具。在
库中真正缺少的是一个基本的节点访问类,比如由Python ^{} module提供的类,或者一个树节点遍历器,同样类似于^{} module provides。幸运的是,这两种方法都很容易塑造自己: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节点:
^{pr2}$
或者statement, = sqlparse.parse(query)
cases = []
for token in walk_tokens(statement):
if isinstance(token, sqlparse.sql.Case):
cases.append(token.get_cases())
在本例中,walk_tokens()和NodeVisitor模式之间的差异可以忽略不计,但是我们只是为每个CASE语句提取分离的标记,而不处理WHEN ... THEN ...标记。在NodeVisitor模式中,您可以在当前的visitor实例上设置更多的属性为“switch gears”,并在更多的visit_....方法中捕获有关这些子树标记的更多信息,这可能比在生成器上嵌套的for循环更容易遵循。在
另一方面,使用walk_tokens()生成器,如果创建一个单独的变量来引用生成器,则可以将迭代移交给helper函数:all_tokens = walk_tokens(stamement)
for token in walk_tokens(statement):
if isinstance(token, sqlparse.sql.Case):
branches = extract_branches(all_tokens)
其中extract_branches将进一步迭代,直到到达case语句的末尾。在