MySQLdb 引數處理的坑

2021-06-22 00:18:14 字數 2980 閱讀 8440

前幾天又有同事掉進了給 sql 的 in 條件傳參的坑,就像 select col1, col2 from table1 where id in (1, 2, 3) 這類 sql,如果是乙個可變的列表作為 in 的引數,那這個引數應該怎麼傳呢?

我見過至少這麼幾種:

id_list = [1, 2, 3]

cursor.execute('select col1, col2 from table1 where id in (%s)', id_list)

這種方式是語法錯誤的,原因是 mysqldb 做字串格式化時佔位符和引數個數不匹配。

id_list = [1, 2, 3]

cursor.execute('select col1, col2 from table1 where id in (%s)', (id_list,))

這種方式語法是正確的,但語義是錯誤的,因為生成的 sql 是 select col1, col2 from table1 where id in ((『1』, 『2』, 『3』))

id_list = [1, 2, 3]

id_list = ','.join([str(i) for i in id_list])

cursor.execute('select col1, col2 from table1 where id in (%s)', id_list)

這種方式語義也是錯誤的,因為生成的 sql 是 select col1, col2 from table1 where id in (『1,2,3』)

這三種是第一次使用 mysqldb 給 in 傳參時犯的最多的錯誤,大多數人遇到第一種錯和掉進後兩個坑之後,轉而採用了下面的方式:

id_list = [1, 2, 3]

id_list = ','.join([str(i) for i in id_list])

cursor.execute('select col1, col2 from table1 where id in (%s)' % id_list)

這個方式對於可信的引數(比如自己生成的列表:range(1, 10, 2))來說可以用,但由於引數未經 escape,對於從使用者端接受的不可信引數來說,存在 sql 注入的風險。

嚴防 sql 注入的問題時刻都不能鬆懈,於是就有了這樣的改進版本:

id_list = [1, 2, 3]

id_list = ','.join([str(cursor.connection.literal(i)) for i in id_list])

cursor.execute('select col1, col2 from table1 where id in (%s)' % id_list)

這個方式控制了 sql 注入問題的滋生,但由於cursor.connection.literal是內部介面,並不推薦從外部使用。

然後就有了這樣的方式:

id_list = [1, 2, 3]

arg_list = ','.join(['%s'] * len(id_list))

cursor.execute('select col1, col2 from table1 where id in (%s)' % arg_list, id_list)

這個方式是先生成與引數個數相同的 %s 佔位,拼出 『select col1, col2 from table1 where id in (%s,%s,%s)』 這樣的 sql,然後使用安全的方式來傳參。

就是想傳乙個引數而已,怎麼會這麼麻煩呢?觸令喪慘!

說來汗顏,一直沒有認真完整讀過 mysqldb 的**,一直想當然地以為 mysqldb 是不支援給 in 傳參的,直到這次又有同事掉坑我才讀了 mysqldb escape 部分的**,然後發現,mysqldb 是在很多態別的 python object 和 sql 支援的型別之間做自動轉換的,比如 mysqldb 會對 list 和 tuple 內的元素逐個進行 escape,生成乙個 tuple,因此這才是正確的給 in 傳參的方式:

id_list = [1, 2, 3]

cursor.execute('select col1, col2 from table1 where id in %s', (id_list,))

可以把 mysqldb 處理引數的過程簡化描述為:

對引數 (id_list,) 做 escape 得到 ((『1』, 『2』, 『3』),)

用 escape 過的引數對 sql 進行格式化:』select col1, col2 from table1 where id in %s』 % ((『1』, 『2』, 『3』),),得到完整 sql:』select col1, col2 from table1 where id in (『1』, 『2』, 『3』)

mysqldb 支援對各種型別的 python object 進行轉換和 escape,感興趣的同學可以看看mysqldb.converters_mysql.c*_escape*系列的函式,另外 mysqldb 也支援自定義轉換規則,參見mysqldb.connectconv引數。

型別引數的坑

大家都知道const 型別引數與 型別引數的區別是能否通過形參更改實參 在傳送什麼樣子的引數上面二者還有一點區別,舉個例子 void generate func string s cout this is generate generate func sssssss 編譯錯誤 無論哪個呼叫都使用了隱...

Python預設引數的坑

定義乙個函式,傳入乙個list,新增乙個end再返回 def add end l return l 正常呼叫時,結果似乎不錯 print add end 1,2,3 1,2,3,end 使用預設引數呼叫時,一開始結果也是對的,但是再次呼叫時,結果就不對了 print add end end prin...

關於mysqldb 的使用

1.連線 conn mysqldb.connect host,port,user,passwd 根據所要連線的資料庫進行設定 2.切換資料庫 conn.select db db name 3.關閉資料庫 conn.close 4.關於fetch的結果處理 fetch 得到的結果是tuple形式,可轉...