#python #database #sqlite
Вопрос:
У меня есть следующий код на Python:
cursor.execute("INSERT INTO table VALUES var1, var2, var3,")
где var1
-целое число, var2
а var3
-строки.
Как я могу написать имена переменных без включения их в Python как части текста запроса?
Ответ №1:
cursor.execute("INSERT INTO table VALUES (%s, %s, %s)", (var1, var2, var3))
Обратите внимание, что параметры передаются в виде кортежа.
API базы данных выполняет правильное экранирование и кавычки переменных. Будьте осторожны, не используйте оператор форматирования строк ( %
), потому что
- он не делает никаких побегов или цитирования.
- он подвержен неконтролируемым атакам в строковом формате, например, инъекциям SQL.
Комментарии:
1. Интересно, почему он работает с переменными по отдельности,а не в массиве (var1,var2, var3)?
2. Согласно спецификациям API БД, похоже, что это может быть в любом случае: python.org/dev/peps/pep-0249
3. @thekashyap Прочитайте еще раз внимательно. Что небезопасно, так это использование оператора форматирования строк
%
. На самом деле, я так и говорю в ответе.4. Отклонено, потому что в ответе сказано не использовать
%
, но использует его три раза. Было бы здорово получить дополнительные объяснения.5.@eric в ответе говорится, что не используйте
%
оператор для форматирования строки. Те%
, что в строке, используютсяcursor.execute
напрямую, и поскольку он знает, что генерирует SQL, он может сделать больше для вашей защиты.
Ответ №2:
Различным реализациям DB-API Python разрешено использовать разные заполнители, поэтому вам нужно будет выяснить, какой из них вы используете-это может быть (например, с 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)
где вы передаете дикт вместо карты в качестве второго аргумента execute
). Проверьте paramstyle
строковую константу в используемом вами модуле DB API и найдите параметр paramstyle по адресу http://www.python.org/dev/peps/pep-0249/ чтобы увидеть, каковы все стили передачи параметров!
Комментарии:
1. Можно ли сделать то же самое, но с помощью внешнего сценария SQL?
Ответ №3:
Многими способами. НЕ используйте самый очевидный ( %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))
Комментарии:
1. Некоторые реализации DB-API фактически используют %s для своих переменных-особенно psycopg2 для PostgreSQL. Это не следует путать (хотя это легко) с использованием %s с оператором % для замены строки. Я был бы очень рад, если бы для переносимости у нас был просто определенный стандартный способ указания параметров SQL для DB-API.
Ответ №4:
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))
Комментарии:
1. На самом деле, это не побег SQL. Это привязка к переменной, которая намного проще и более прямая. Значения привязываются к оператору SQL после синтаксического анализа, что делает его невосприимчивым к любой инъекционной атаке.
2. ну, будет ли это экранирование SQL или привязка переменных, зависит от того, насколько хорош или плох ваш драйвер сервера базы данных / DB-API. Я видел некоторые реальные, широко развернутые производственные базы данных, в которых драйвер DB-API просто ускользает, вместо того, чтобы хранить данные и код вне диапазона на проводе. Излишне говорить, что я не очень уважаю эти так называемые «базы данных».
Ответ №5:
Синтаксис для предоставления одного значения может сбить с толку неопытных пользователей Python.
Учитывая запрос
INSERT INTO mytable (fruit) VALUES (%s)
Как правило*, передаваемое значение cursor.execute
все равно должно быть кортежем, даже если оно является одноэлементным, поэтому мы должны предоставить кортеж из одного элемента, например: (value,)
.
cursor.execute("""INSERT INTO mytable (fruit) VALUES (%s)""", ('apple',))
Передача одной строки
cursor.execute("""INSERT INTO mytable (fruit) VALUES (%s)""", ('apple'))
приведет к ошибке, которая зависит от соединителя DB-API, например
- psycopg2:
Ошибка типа: не все аргументы преобразуются во время форматирования строки
- sqlite3
sqlite3.Ошибка программирования: Неверное количество предоставленных привязок. В текущем заявлении используется 1, и в нем указано 5
- mysql.коннектор
mysql.connector.ошибки.Ошибка программирования: 1064 (42000): У вас ошибка в синтаксисе SQL;
* Соединитель pymysql обрабатывает один строковый параметр без ошибок. Однако лучше обернуть строку в кортеж, даже если это один, потому что
- вам не нужно будет менять код, если вы переключите пакет соединителя
- вы сохраняете согласованную мысленную модель параметров запроса, представляющую собой последовательность объектов, а не один объект.