用Python解析SQL
我想在一个非关系型数据存储上创建一个SQL接口。虽然这个数据存储不是关系型的,但以关系型的方式访问数据是有意义的。
我正在考虑使用ANTLR来生成一个抽象语法树(AST),这个树可以表示SQL作为关系代数表达式。然后通过评估或遍历这棵树来返回数据。
我之前从来没有实现过解析器,所以我想请教一下,如何最好地实现一个SQL解析器和评估器。
更新:
我用pyparsing实现了一个简单的SQL解析器。结合Python代码来对我的数据存储执行关系操作,这个过程相对简单。
正如我在评论中提到的,这个练习的目的是为了让数据可以被报表引擎使用。为此,我可能需要实现一个ODBC驱动程序。这可能会需要很多工作。
6 个回答
TwoLaid的Python SQL解析器对我来说非常好用。它是用C语言写的,需要编译。这个解析器很强大,可以把每个语句的各个部分分开。
https://github.com/TwoLaid/python-sqlparser
我用它来提取查询中的列名,以便在报告的标题中使用。下面是一个例子。
import sqlparser
def get_query_columns(sql):
'''Return a list of column headers from given sqls select clause'''
columns = []
parser = sqlparser.Parser()
# Parser does not like new lines
sql2 = sql.replace('\n', ' ')
# Check for syntax errors
if parser.check_syntax(sql2) != 0:
raise Exception('get_query_columns: SQL invalid.')
stmt = parser.get_statement(0)
root = stmt.get_root()
qcolumns = root.__dict__['resultColumnList']
for qcolumn in qcolumns.list:
if qcolumn.aliasClause:
alias = qcolumn.aliasClause.get_text()
columns.append(alias)
else:
name = qcolumn.get_text()
name = name.split('.')[-1] # remove table alias
columns.append(name)
return columns
sql = '''
SELECT
a.a,
replace(coalesce(a.b, 'x'), 'x', 'y') as jim,
a.bla as sally -- some comment
FROM
table_a as a
WHERE
c > 20
'''
print get_query_columns(sql)
# output: ['a', 'jim', 'sally']
这篇Reddit帖子提到了一些现成的解决方案,其中包括python-sqlparse,还有其他几个链接。
我对这个问题研究了很久。Python-sqlparse 是一个不进行验证的解析器,这其实不太符合你的需求。antlr 中的例子需要很多工作才能转换成 Python 中好用的抽象语法树(AST)。SQL 的标准语法可以在这里找到,但自己转换这些语法几乎就像是一份全职工作,而且你可能只需要其中的一小部分,比如不需要连接查询。你也可以看看gadfly(一个 Python SQL 数据库),但我没有选择它,因为它使用了自己的解析工具。
对我来说,我基本上只需要一个 where 子句。我尝试过booleneo(一个布尔表达式解析器),它是用 pyparsing 写的,但最后还是决定从头开始使用 pyparsing。Mark Rushakoff 在 Reddit 帖子中的第一个链接给出了一个使用它的 SQL 示例。Whoosh 是一个全文搜索引擎,也使用了它,不过我还没有查看源代码来了解具体是怎么做的。
Pyparsing 非常容易使用,你可以很简单地自定义它,使其不完全和 SQL 一样(大部分语法你可能用不到)。我不喜欢 ply,因为它使用了一些命名约定的魔法。
总之,试试 pyparsing 吧,它很可能足够强大来满足你的需求,而且与 Python 的简单集成(有简单的回调和错误处理)会让整个过程变得相当轻松。