Подстановка параметров для предложения SQLite "IN" - PullRequest
41 голосов
/ 21 августа 2009

Я пытаюсь использовать подстановку параметров с SQLite в Python для предложения IN. Вот полный пример, который демонстрирует:

import sqlite3

c = sqlite3.connect(":memory:")
c.execute('CREATE TABLE distro (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT)')

for name in 'Ubuntu Fedora Puppy DSL SuSE'.split():
  c.execute('INSERT INTO distro (name) VALUES (?)', [ name ] )

desired_ids = ["1", "2", "5", "47"]
result_set = c.execute('SELECT * FROM distro WHERE id IN (%s)' % (", ".join(desired_ids)), ())
for result in result_set:
  print result

Распечатывается:

(1, u'Ubuntu ') (2, Федора) (5, u'SuSE ')

Поскольку в документации указано, что «[y] вы не должны собирать ваш запрос, используя строковые операции Python, потому что это небезопасно; это делает вашу программу уязвимой для атаки SQL-инъекцией», - я надеюсь использовать подстановку параметров. 1012 *

Когда я пытаюсь:

result_set = c.execute('SELECT * FROM distro WHERE id IN (?)', [ (", ".join(desired_ids)) ])

Я получаю пустой набор результатов, и когда я пытаюсь:

result_set = c.execute('SELECT * FROM distro WHERE id IN (?)', [ desired_ids ] )

Я получаю:

InterfaceError: Ошибка привязки параметра 0 - возможно, неподдерживаемый тип.

Хотя я надеюсь, что любой ответ на эту упрощенную задачу сработает, я хотел бы отметить, что фактический запрос, который я хочу выполнить, находится во вложенном подзапросе с двойным вложением. Для остроумия:

UPDATE dir_x_user SET user_revision = user_attempted_revision 
WHERE user_id IN 
    (SELECT user_id FROM 
        (SELECT user_id, MAX(revision) FROM users WHERE obfuscated_name IN 
            ("Argl883", "Manf496", "Mook657") GROUP BY user_id
        ) 
    )

Ответы [ 5 ]

60 голосов
/ 21 августа 2009

Вам нужно правильное количество ? с, но это не представляет риска инъекции sql:

>>> result_set = c.execute('SELECT * FROM distro WHERE id IN (%s)' %
                           ','.join('?'*len(desired_ids)), desired_ids)
>>> print result_set.fetchall()
[(1, u'Ubuntu'), (2, u'Fedora'), (5, u'SuSE')]
22 голосов
/ 17 августа 2010

Согласно http://www.sqlite.org/limits.html (элемент 9), SQLite не может (по умолчанию) обрабатывать более 999 параметров для запроса, поэтому решения здесь (создание необходимого списка заполнителей) завершатся неудачно, если у вас есть тысячи предметов, которые вы ищете IN. Если это так, то вам нужно разбить список, а затем обойти его частями и объединить результаты самостоятельно.

Если вам не нужны тысячи элементов в вашем предложении IN, то решение Алекса - это способ сделать это (и похоже, как это делает Django).

11 голосов
/ 21 августа 2009

Обновление: это работает:

import sqlite3

c = sqlite3.connect(":memory:")
c.execute('CREATE TABLE distro (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT)')

for name in 'Ubuntu Fedora Puppy DSL SuSE'.split():
  c.execute('INSERT INTO distro (name) VALUES (?)', ( name,) )

desired_ids = ["1", "2", "5", "47"]
result_set = c.execute('SELECT * FROM distro WHERE id IN (%s)' % ("?," * len(desired_ids))[:-1], desired_ids)
for result in result_set:
  print result

Проблема заключалась в том, что вам нужно иметь один? для каждого элемента в списке ввода.

Оператор ("?," * len(desired_ids))[:-1] создает повторяющуюся строку «?», Затем обрезает последнюю запятую. так что для каждого элемента в required_ids есть один знак вопроса.

3 голосов
/ 21 августа 2009

Я всегда заканчиваю тем, что делаю что-то вроде этого:

query = 'SELECT * FROM distro WHERE id IN (%s)' % ','.join('?' for i in desired_ids)
c.execute(query, desired_ids)

Нет риска инъекции, потому что вы не помещаете строки из required_ids непосредственно в запрос.

0 голосов
/ 28 августа 2009

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...