web-dev-qa-db-fra.com

Analyser SQL avec Python

Je veux créer une interface SQL au-dessus d'un magasin de données non relationnel. Stockage de données non relationnel, mais il est logique d'accéder aux données de manière relationnelle.

Je cherche à utiliser ANTLR pour produire un AST qui représente le SQL comme une expression d'algèbre relationnelle. Ensuite, renvoyez les données par évaluer/marcher sur l'arbre.

Je n'ai jamais implémenté un analyseur avant, et je voudrais donc quelques conseils sur la meilleure façon d'implémenter un analyseur et un évaluateur SQL.

  • L'approche décrite ci-dessus semble-t-elle juste?
  • Y a-t-il d'autres outils/bibliothèques que je devrais étudier? Comme PLY ou Pyparsing .
  • Des pointeurs vers des articles, des livres ou du code source qui m'aideront sont appréciés.

Mise à jour:

J'ai implémenté un analyseur SQL simple en utilisant le pyparsing. Combiné avec Python code qui implémente les opérations relationnelles sur mon magasin de données, c'était assez simple.

Comme je l'ai dit dans l'un des commentaires, l'objectif de l'exercice était de mettre les données à la disposition des moteurs de génération de rapports. Pour ce faire, je devrai probablement implémenter un pilote ODBC. C'est probablement beaucoup de travail.

44
codeape

J'ai étudié cette question de manière assez approfondie. Python-sqlparse est un analyseur non validant qui n'est pas vraiment ce dont vous avez besoin. Les exemples dans antlr nécessitent beaucoup de travail pour être convertis en Nice ast en python. Les grammaires SQL standard sont ici , mais ce serait un travail à plein temps de les convertir vous-même et il est probable que vous n'en ayez besoin que d'un sous-ensemble, c'est-à-dire sans jointure. Vous pouvez également essayer de consulter la gadfly (une python), mais je l'ai évitée car ils utilisaient leur propre outil d'analyse.

Pour mon cas, je n'avais essentiellement besoin que d'une clause where. J'ai essayé booleneo (un analyseur d'expression booléenne) écrit avec pyparsing mais j'ai fini par utiliser pyparsing à partir de zéro. Le premier lien dans le post reddit de Mark Rushakoff donne un exemple sql en l'utilisant. Whoosh un moteur de recherche plein texte l'utilise également mais je n'ai pas regardé la source pour voir comment.

Le Pyparsing est très facile à utiliser et vous pouvez très facilement le personnaliser pour qu'il ne soit pas exactement le même que sql (la plupart de la syntaxe dont vous n'aurez pas besoin). Je n'aimais pas ply car il utilise de la magie en utilisant des conventions de dénomination.

En bref, essayez le pyparsing, il sera probablement assez puissant pour faire ce dont vous avez besoin et l'intégration simple avec python (avec des rappels faciles et une gestion des erreurs) rendra l'expérience assez indolore.

37
David Raznick

Ce post reddit suggère Python-sqlparse comme une implémentation existante, parmi quelques autres liens.

11
Mark Rushakoff

TwoLaid's Python SQL Parser fonctionne très bien pour mes besoins. Il est écrit en C et doit être compilé. Il est robuste. Il analyse les éléments individuels de chaque clause.

https://github.com/TwoLaid/python-sqlparser

Je l'utilise pour analyser les noms de colonnes de requêtes à utiliser dans les en-têtes de rapport. Voici un exemple.

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

Bien sûr, il peut être préférable d'utiliser python-sqlparse sur Google Code

MISE À JOUR: Maintenant, je vois que cela a été suggéré - je conviens que cela vaut la peine:

1
Barton