web-dev-qa-db-fra.com

Comment voir la vraie requête SQL dans Python cursor.execute en utilisant pyodbc et MS-Access

J'utilise le code suivant dans Python (avec pyodbc pour une base MS-Access).

cursor.execute("select a from tbl where b=? and c=?", (x, y))

C'est Ok mais, à des fins de maintenance, j'ai besoin de connaître la chaîne SQL complète et exacte envoyée à la base de données.
Est-ce possible et comment?

42
philnext

La réponse est non. Je poste ma question sur le code Google du projet (et dans le groupe Google). La réponse est:

Commentaire n ° 1 sur le problème 163 par l ... @ deller.id.au: cursor.mogrify retourne la chaîne de requête http://code.google.com/p/pyodbc/issues/detail?id=16

Pour référence, voici un lien vers la documentation pyscopg de leur méthode de curseur "mogrify" à laquelle le journaliste fait référence: http://initd.org/psycopg/docs/cursor.html#cursor.mogrify

pyodbc n'effectue pas de telles traductions du SQL: il transmet le SQL paramétré directement au pilote ODBC textuellement. Le seul traitement impliqué est la traduction des paramètres de Python des objets aux types C pris en charge par l'API ODBC.

Certaines transformations sur SQL peuvent être effectuées dans le pilote ODBC avant d'être envoyées au serveur (par exemple, Microsoft SQL Native Client le fait)), mais ces transformations sont masquées par pyodbc.

Par conséquent, je pense qu'il n'est pas possible de fournir une fonction de mogrifie dans pyodbc.

6
philnext

Il diffère selon le conducteur. Voici deux exemples:

import MySQLdb
mc = MySQLdb.connect()
r = mc.cursor()
r.execute('select %s, %s', ("foo", 2))
r._executed
"select 'foo', 2"

import psycopg2
pc = psycopg2.connect()
r = pc.cursor()
r.execute('select %s, %s', ('foo', 2))
r.query
"select E'foo', 2"
42
samplebias

Vous pouvez utiliser print cursor._last_executed Pour obtenir la dernière requête exécutée.

Lisez this répondez que vous pouvez également utiliser print cursor.mogrify(query,list) pour voir la requête complète avant ou après l'exécution.

8
joseph

Pour déboguer purpuse, j'ai créé une fonction de vérification qui remplace simplement? avec les valeurs de requête ... ce n'est pas de la haute technologie :) mais ça marche! :RÉ

def check_sql_string(sql, values):
    unique = "%PARAMETER%"
    sql = sql.replace("?", unique)
    for v in values: sql = sql.replace(unique, repr(v), 1)
    return sql

query="""SELECT * FROM dbo.MA_ItemsMonthlyBalances
                   WHERE Item = ? AND Storage = ? AND FiscalYear = ? AND BalanceYear = ? AND Balance = ? AND BalanceMonth = ?"""
values = (1,2,"asdasd",12331, "aas)",1)

print(check_sql_string(query,values))

Le résultat:

SELECT * FROM dbo.MA_ItemsMonthlyBalances WHERE Item = 1 AND Storage = 2 AND FiscalYear = 'asdasd' AND BalanceYear = 12331 AND Balance = 'aas') AND BalanceMonth = 1

Avec cela, vous pouvez vous connecter ou faire ce que vous voulez:

rowcount = self.cur.execute(query,values).rowcount
logger.info(check_sql_string(query,values))

Si vous avez juste besoin d'ajouter une exception à la fonction.

3
piertoni

Selon le pilote que vous utilisez, cela peut ou non être possible. Dans certaines bases de données, les paramètres (?s) sont simplement remplacés, comme le suggère la réponse de user589983 (bien que le pilote devra faire des choses comme citer des chaînes et échapper des guillemets dans ces chaînes, pour aboutir à une instruction exécutable).

D'autres pilotes demanderont à la base de données de compiler ("préparer") l'instruction, puis lui demanderont d'exécuter l'instruction préparée en utilisant les valeurs données. C'est de cette manière que l'utilisation d'instructions préparées ou paramétrées permet d'éviter les injections SQL - au moment où l'instruction s'exécute, la base de données "sait" ce qui fait partie du SQL que vous souhaitez exécuter et ce qui fait partie d'une valeur utilisée dans cette déclaration.

A en juger par un survol rapide de la documentation PyODBC , il ne semble pas que l'exécution du SQL réel soit possible, mais je peux me tromper.

2
dcrosta

Je vérifierais le curseur.

def log_queries(cur):
    def _query(q):
        print q # could also use logging
        return cur._do_query(q)
    cur._query = _query

conn = MySQLdb.connect( read_default_file='~/.my.cnf' )
cur = conn.cursor()
log_queries(cur)
cur.execute('SELECT %s, %s, %s', ('hello','there','world'))

Il est très dépendant de MySQLdb (et pourrait se casser dans les versions ultérieures). Cela fonctionne car cur._query appelle actuellement simplement calls._do_query et renvoie son résultat.

1
steveayre