將MySQL與Python一起使用時(shí),您可能希望使用mysqlclient庫,這是大多數(shù)人所做的且效果甚好。或者,如果您正在使用官方的MySQL 8 Connector/Python包,情況會(huì)略有不同,但可能已經(jīng)在MySQL 8中支持的功能還尚未出現(xiàn)在mysqlclient中。
您的SQL可能是手動(dòng)編寫的,也可能是使用SQL Alchemy,Django或其他包生成的。如果是后者,為了便于交互式調(diào)試,查看發(fā)送到數(shù)據(jù)庫的實(shí)際SQL字符串的方法可能會(huì)很有用。
使用 mysqlclient
如果mysqlclient與Python一起使用,則Cursor類內(nèi)部以該類(github源碼)中的_query(self, q)方法將SQL發(fā)送到服務(wù)器。
該方法_query(self, q)本身會(huì)更新cursor的_executed成員,存儲(chǔ)發(fā)送到服務(wù)器的文本查詢字符串。但它僅在發(fā)送查詢后才會(huì)這樣做。出錯(cuò)時(shí),將引發(fā)異常且_executed的更新失敗(github源碼 )。
若要訪問實(shí)際的查詢字符串,請(qǐng)定義一個(gè)類DebugCursor,并使用連接**kwargs指定它。在DebugCursor中,這么做是有必要的。
import MySQLdb
import MySQLdb.cursors
class DebugCursor(MySQLdb.cursors.DictCursor):
def _query(self, q):
print(f"Debug: {q}")
super()._query(q)
db_config = dict(
host="localhost",
user="kris",
passwd="secret",
db="kris",
cursorclass=DebugCursor, # referenced class must be defined before
)
db = MySQLdb.connect(**db_config)
c = db.cursor(()
sql = "select d from testtable where id = 3"
c.execute(sql)
print(c.fetchall())
在類DebugCursor中,我們選擇繼承ursor。重寫_query(self, q)方法,打印查詢字符串q,然后調(diào)用父類實(shí)現(xiàn)。
輸出:
$ python3 probe.py
Debug: b'select d from testtable where id = 3'
({'d': 'drei'},)
即使查詢字符串無效或包含語法錯(cuò)誤,我們也可以使用它來跟蹤所有被執(zhí)行之前的文本SQL。
使用 MySQL Connector/Python
如果您使用Oracle MySQL Connector/Python連接到數(shù)據(jù)庫,則可以在內(nèi)部使用Cython或協(xié)議的Pure Python實(shí)現(xiàn)。
這兩種實(shí)現(xiàn)的行為略有不同。只有Pure Python實(shí)現(xiàn)可以在所有情況下輕松調(diào)試。因此,請(qǐng)務(wù)必使用**kwargs連接指定use_pure=True。
原始SQL語句將在cursor的_executed成員中找到。如果您使用的是多語句,它們將記錄在_executed_list中。
我們寫為:
import mysql.connector
import mysql.connector.errors
db_config = dict(
host="127.0.0.1",
user="kris",
passwd="geheim",
db="kris",
use_pure=True,
)
db = mysql.connector.connect(**db_config)
c = db.cursor()
print("=== Valid SQL: ")
sql = "select d from testtable where id = 3"
c.execute(sql)
print(f"Debug: {c._executed}")
print(c.fetchall())
print()
print("=== Invalid SQL: ")
sql = "syntaxerror d from testtable where id = 3"
try:
c.execute(sql)
except mysql.connector.errors.ProgrammingError as e:
print(f"Debug: {c._executed}")
print(f"Error: {e}")
輸出如下所示:
python3 probe.py
=== Valid SQL:
Debug: b'select d from testtable where id = 3'
[('drei',)]
=== Invalid SQL:
Debug: b'syntaxerror d from testtable where id = 3'
Error: 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'syntaxerror d from testtable where id = 3' at line 1
同樣,我們可以使用它來跟蹤原始SQL并識(shí)別實(shí)際生成的SQL語法,這樣會(huì)更容易。這將允許我們選取文本SQL字符串,并以交互方式進(jìn)行調(diào)試。
如果SQL格式不正確,則在不帶use_pure=True的情況下運(yùn)行時(shí),相同的程序?qū)o法正確更新c._executed。它將生成以下輸出,這對(duì)于調(diào)試毫無價(jià)值:
$ python3 probe.py
=== Valid SQL:
Debug: b'select d from testtable where id = 3'
[('drei',)]
=== Invalid SQL:
Debug: b'select d from testtable where id = 3'
Error: 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'syntaxerror d from testtable where id = 3' at line 1
請(qǐng)注意,c._executed是怎樣做到仍然保存前一個(gè)語句,而不是實(shí)際出錯(cuò)的語句。
原文標(biāo)題:Debugging SQL in Python
原文作者:Kristian K?hntopp
原文鏈接:https://blog.koehntopp.info/2022/03/24/debugging-sql-in-python.html#using-mysqlclient




