2009-09-08 401 views
38

我想在非關係數據存儲上創建一個SQL接口。非關係數據存儲,但以關係方式訪問數據是有意義的。用Python解析SQL

我正在研究使用ANTLR來產生一個表示SQL作爲關係代數表達式的AST。然後通過評估/行走樹來返回數據。

我從來沒有實現過一個解析器,因此我想就如何最好地實現SQL解析器和評估器提供一些建議。

  • 上面描述的方法聽起來是對的嗎?
  • 我應該看看有其他的工具/庫嗎?像PLYPyparsing
  • 指向將幫助我的文章,書籍或源代碼的讚賞。

更新:

我使用pyparsing實現了一個簡單的SQL語法分析程序。結合對我的數據存儲實現關係操作的Python代碼,這非常簡單。

正如我在其中一條評論中所說的,練習的要點是使數據可用於報告引擎。爲此,我可能需要實現一個ODBC驅動程序。這可能是很多工作。

+2

爲什麼對對象施加SQL限制?會得到什麼? OQL有什麼問題? http://en.wikipedia.org/wiki/Object_Query_Language – 2009-09-08 20:00:16

+7

待獲得:大量報告工具可以使用的查詢界面。我打算在客戶端上實現一個ODBC驅動程序。因此,業務用戶可以使用Crystal Reports,Excel等從數據存儲中獲取數據。 OQL雖然可能是一種很好的查詢語言(我從來沒有用過),但它並不像SQL那樣廣泛。 – codeape 2009-09-09 11:29:19

+1

+1兩者:OO數據庫最大的問題之一就是缺乏報表引擎:( – van 2009-09-19 11:43:27

回答

33

我已經很深入地研究了這個問題。 Python-sqlparse是一個非驗證解析器,它不是您真正需要的。 antlr中的例子需要很多工作才能轉換爲python中的一個很好的ast。 sql標準語法是here,但它將是一個全職工作來自己轉換它們,並且很可能您只需要它們的一個子集,即沒有聯接。你也可以試試看gadfly(一個python sql數據庫),但是我避免了它,因爲他們使用了他們自己的解析工具。

對我而言,我只需要一個where子句。我嘗試用pyparsing編寫booleneo(一個布爾表達式解析器),但最終從頭開始使用pyparsing。 Mark Rushakoff的reddit文章中的第一個鏈接給出了一個使用它的sql示例。 Whoosh全文搜索引擎也使用它,但我沒有看過源,看看如何。

Pyparsing非常易於使用,您可以非常容易地將其定製爲與sql(大多數不需要的語法)完全相同。我不喜歡它,因爲它使用一些使用命名約定的魔法。

簡而言之,pyparsing一個嘗試,它很可能足夠強大,可以完成你所需要的任務,並且簡單地與python進行集成(使用簡單的回調和錯誤處理)將使得體驗變得非常輕鬆。

+1

感謝您分享您的體驗。初始的,非常有限的python-sqlparse測試,似乎我可能會使用它,我將嘗試使用python-sqlparse中''parse''函數的返回值,但是我會研究pyparsing in任何情況下。 – codeape 2009-09-09 11:25:01

+1

Pyparsing是一個很好的工具,有很多解析sql的例子。 – 2009-09-09 21:30:59

+2

pyparsing wiki上的海報(http://pyparsing.wikispaces.com/message/view/home/14105203)剛剛報告完成一個SQL SELECT語法分析器 - 也許你可以聯繫他/她尋求幫助,建議,甚至是代碼 – PaulMcG 2009-09-11 02:51:25

9

This reddit post建議Python-sqlparse作爲一個現有的實現,在其他一些鏈接中。

+0

感謝您的建議。Python-sqlparse看起來很有趣,我會試試看。 – codeape 2009-09-09 11:19:52

2

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']