Вопрос-Ответ

Imploding a list for use in a Python MySQL IN clause

Создание списка для использования в предложении Python MySQL IN

Я знаю, как сопоставить список со строкой:

foostring = ",".join( map(str, list_of_ids) )

И я знаю, что могу использовать следующее, чтобы вставить эту строку в предложение IN:

cursor.execute("DELETE FROM foo.bar WHERE baz IN ('%s')" % (foostring))

Как я могу сделать то же самое безопасно (избегая SQL-инъекции), используя базу данных MySQL?

В приведенном выше примере, поскольку foostring не передается в качестве аргумента для выполнения, он уязвим. Мне также приходится заключать его в кавычки и экранировать за пределами библиотеки MySQL.

(Есть связанный с Stack Overflow вопрос, но ответы, перечисленные там, либо не работают для базы данных MySQL, либо уязвимы для SQL-инъекции.)

Переведено автоматически
Ответ 1

Используйте list_of_ids напрямую:

format_strings = ','.join(['%s'] * len(list_of_ids))
cursor.execute("DELETE FROM foo.bar WHERE baz IN (%s)" % format_strings,
tuple(list_of_ids))

Таким образом, вам не придется заключать себя в кавычки и избегать всех видов SQL-инъекций.

Обратите внимание, что данные (list_of_ids) передаются непосредственно в драйвер MySQL в качестве параметра (не в тексте запроса), поэтому никакого внедрения не происходит. Вы можете оставить в строке любые символы, которые хотите; нет никакой необходимости удалять символы или заключать их в кавычки.

Ответ 2

Принятый ответ становится беспорядочным, когда у нас много параметров или если мы хотим использовать именованные параметры.

После некоторых испытаний,

ids = [5, 3, ...]  # List of ids
cursor.execute('''
SELECT
...
WHERE
id IN %(ids)s
AND created_at > %(start_dt)s
'''
, {
'ids': tuple(ids), 'start_dt': '2019-10-31 00:00:00'
})

Он был протестирован с Python 2.7 и pymysql 0.7.11.

Ответ 3

Похоже, что это все еще остается проблемой с Python3 в 2021 году, как указано в комментарии Rubms к ответу markk.

Добавление около 9 строк кода к методу "_process_params_dict" в "cursor.py" в пакете mysql connector для обработки кортежей решило проблему для меня:

def _process_params_dict(self, params):
"""Process query parameters given as dictionary"""
try:
to_mysql = self._connection.converter.to_mysql
escape = self._connection.converter.escape
quote = self._connection.converter.quote
res = {}
for key, value in list(params.items()):
if type(value) is tuple: ### BEGIN MY ADDITIONS
res[key.encode()] = b''
for subvalue in value:
conv = subvalue
conv = to_mysql(conv)
conv = escape(conv)
conv = quote(conv)
res[key.encode()] = res[key.encode()] + b',' + conv if len(res[key.encode()]) else conv
else: ### END MY ADDITIONS
conv = value
conv = to_mysql(conv)
conv = escape(conv)
conv = quote(conv)
res[key.encode()] = conv
except Exception as err:
raise errors.ProgrammingError(
"Failed processing pyformat-parameters; %s" % err)
else:
return res
Ответ 4

Возможно, я немного опоздал с ответом на вопрос, но я наткнулся на похожую проблему, но я хотел использовать dict именованных параметров вместо кортежа (потому что, если я хочу изменить параметры, чтобы добавить или удалить некоторые, я не хочу переконструировать кортеж, перепутать порядок может быть очень легко и привести к ошибкам ...).

Мое решение заключалось в форматировании строки запроса, чтобы разбить параметр на несколько параметров, а затем сконструировать параметр dict с этими новыми параметрами:

from typing import Iterable

query = """
SELECT *
FROM table
WHERE id IN (%(test_param)s)
"""


parameters = {"test_param": [1, 2, 3])

new_params = {}

for k, v in parameters.items():
if isinstance(v, Iterable):
iterable_params = {f"{k}_{i}": value for i, value in enumerate(v)}
iterable_params_formatted = [f"%({k}_{i})s" for i in range(0, len(v))]
query = query.replace(f"%({k})s", ", ".join(iterable_params_formatted))
new_params.update(iterable_params)
else:
new_params[k] = v

print(query)
print(new_params)

Результат:

> SELECT *
FROM table
WHERE id IN (%(test_param_0)s, %(test_param_1)s, %(test_param_2)s)

> {'test_param_0': 1, 'test_param_1': 2, 'test_param_2': 3}

Можно было бы сделать лучше, но я не смог найти решение, использующее набор именованных параметров вместо упорядоченного кортежа.

2024-01-01 09:58 python