Как использовать переменные в операторе SQL в Python?

94

Хорошо, я не настолько разбираюсь в Python.

У меня есть следующий код Python:

cursor.execute("INSERT INTO table VALUES var1, var2, var3,")

где var1целое число,var2 & var3- строки.

Как я могу написать имена переменных без включения их Python в текст запроса?

user111606
источник

Ответы:

107
cursor.execute("INSERT INTO table VALUES (%s, %s, %s)", (var1, var2, var3))

Обратите внимание, что параметры передаются в виде кортежа.

API базы данных выполняет правильное экранирование и цитирование переменных. Будьте осторожны и не используйте оператор форматирования строки ( %), потому что

  1. он не выполняет экранирования или цитирования.
  2. он подвержен атакам неконтролируемого строкового формата, например, SQL-инъекциям .
Айман Хурье
источник
Интересно, почему он работает с варами отдельно, а не в массиве (var1, var2, var3)?
Andomar
Согласно спецификациям DB API, похоже, что это может быть в любом случае: python.org/dev/peps/pep-0249
Ayman Hourieh
9
@thekashyap Прочтите еще раз внимательно. Что небезопасно, так это использование оператора форматирования строки %. Собственно, я так и говорю в ответе.
Ayman Hourieh
мой плохой ... Я представил % вместо ,строки и переменных ... не могу отменить мой голосование по разным причинам ... Я лично хотел бы видеть такие слова, как небезопасный / атака и т. д., упомянутые в описании, где вы говорите не 't use %..
Kashyap
1
@eric в ответе говорится, что не используйте % оператор для форматирования строки. Те, %что указаны в строке, используются cursor.executeнапрямую, и, поскольку он знает, что генерирует SQL, он может сделать больше для вашей защиты.
Марк Рэнсом,
69

Различные реализации Python DB-API могут использовать разные заполнители, поэтому вам нужно выяснить, какой из них вы используете - это может быть (например, с MySQLdb):

cursor.execute("INSERT INTO table VALUES (%s, %s, %s)", (var1, var2, var3))

или (например, с sqlite3 из стандартной библиотеки Python):

cursor.execute("INSERT INTO table VALUES (?, ?, ?)", (var1, var2, var3))

или другие (после VALUESтого, как вы могли иметь (:1, :2, :3), или "именованные стили", (:fee, :fie, :fo)или (%(fee)s, %(fie)s, %(fo)s)где вы передаете dict вместо карты в качестве второго аргумента execute). Проверьте paramstyleстроковую константу в используемом вами модуле API БД и найдите paramstyle на http://www.python.org/dev/peps/pep-0249/, чтобы узнать, что такое все стили передачи параметров!

Алекс Мартелли
источник
Можно ли сделать то же самое, но с внешним скриптом SQL?
Novitoll
49

Много способов. НЕ используйте наиболее очевидный ( %sс %) в реальном коде, он открыт для атак .

Здесь скопировано из pydoc sqlite3 :

# Never do this -- insecure!
symbol = 'RHAT'
c.execute("SELECT * FROM stocks WHERE symbol = '%s'" % symbol)

# Do this instead
t = ('RHAT',)
c.execute('SELECT * FROM stocks WHERE symbol=?', t)
print c.fetchone()

# Larger example that inserts many records at a time
purchases = [('2006-03-28', 'BUY', 'IBM', 1000, 45.00),
             ('2006-04-05', 'BUY', 'MSFT', 1000, 72.00),
             ('2006-04-06', 'SELL', 'IBM', 500, 53.00),
            ]
c.executemany('INSERT INTO stocks VALUES (?,?,?,?,?)', purchases)

Еще примеры, если вам нужно:

# Multiple values single statement/execution
c.execute('SELECT * FROM stocks WHERE symbol=? OR symbol=?', ('RHAT', 'MSO'))
print c.fetchall()
c.execute('SELECT * FROM stocks WHERE symbol IN (?, ?)', ('RHAT', 'MSO'))
print c.fetchall()
# This also works, though ones above are better as a habit as it's inline with syntax of executemany().. but your choice.
c.execute('SELECT * FROM stocks WHERE symbol=? OR symbol=?', 'RHAT', 'MSO')
print c.fetchall()
# Insert a single item
c.execute('INSERT INTO stocks VALUES (?,?,?,?,?)', ('2006-03-28', 'BUY', 'IBM', 1000, 45.00))
Кашьяп
источник
4
Некоторые реализации DB-API фактически используют% s для своих переменных - в первую очередь psycopg2 для PostgreSQL. Это не следует путать (хотя это легко) с использованием% s с оператором% для замены строки. Было бы очень хорошо, если бы для переносимости мы могли просто иметь определенный стандартный способ указания параметров SQL для DB-API.
ThatAintWorking
26

http://www.amk.ca/python/writing/DB-API.html

Будьте осторожны, когда вы просто добавляете значения переменных к своим операторам: представьте, что пользователь называет себя ';DROP TABLE Users;'- вот почему вам нужно использовать экранирование sql, которое Python предоставляет вам, когда вы используете cursor.execute достойным образом. Пример в URL-адресе:

cursor.execute("insert into Attendees values (?, ?, ?)", (name,
seminar, paid) )
Numlock
источник
13
На самом деле, это не экранирование SQL. Это привязка переменных, которая намного проще и прямолинейнее. Значения привязываются к оператору SQL после синтаксического анализа, что делает его невосприимчивым к любой атаке с использованием инъекции.
S.Lott
Хорошо, будет ли это экранирование SQL или связывание переменных, зависит от того, насколько хорош ваш сервер базы данных / драйвер DB-API. Я видел некоторые реальные, широко развернутые производственные базы данных, в которых драйвер DB-API просто выполняет экранирование, а не хранит данные и код вне полосы пропускания. Излишне говорить, что я не очень уважаю эти так называемые «базы данных».
Чарльз Даффи,