Проект действительно немного недокументирован.Я посмотрел на примеры и немного отсканировал исходный код.К сожалению, в документацию не включены все методы классов Token
и TokenList
, которые полезны для этой задачи.
Например, важным, но опущенным методом является метод TokenList.get_sublists()
, что позволяет вам легче просматривать вложенные списки токенов, чем другие методы;метод TokenList.flatten()
дает только несгруппированные токены в дереве, тогда как CASE
является сгруппированным токеном, поэтому, исходя из документации, вам может быть трудно сделать что-то полезное спроанализированное дерево токенов.
Другой удобный метод, который я заметил в кодовой базе, - это метод TokenList._pprint_tree()
, который выводит текущее дерево токенов в стандартный вывод.Это очень полезно при попытке написать код, который анализирует дерево.
В целом мое общее впечатление о sqlparse
состоит в том, что это не столько библиотека разбора, сколько инструмент для переформатирования SQL.Он включает в себя хороший синтаксический анализатор, но не включает инструменты, необходимые для общего использования дерева, которое он создает.
Чего действительно не хватает в библиотеке, так это посетителя базового узлакласс , такой как тот, который предоставляется модулем Python ast
или обходчиком узлов дерева, опять же, как модуль ast
обеспечивает .Либо достаточно просто создать себя, к счастью:
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
узлам:
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
, либо
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
вы бы задали больше атрибутов в текущем экземпляре посетителя для «переключения передач» и собирали дополнительную информацию об этих токенах поддерева в большем количестве методов 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)
где extract_branches
будет повторяться до конца оператора case.