Как явно указать строковое значение (Python DB API/Psycopg2)
По каким-то причинам я хотел бы сделать явное цитирование строкового значения (став частью построенного SQL-запроса), а не ждать неявной цитаты, выполняемой методом cursor.execute
по содержимому его второго параметра.
Под "неявной цитатой" я имею в виду:
value = "Unsafe string"
query = "SELECT * FROM some_table WHERE some_char_field = %s;"
cursor.execute( query, (value,) ) # value will be correctly quoted
Я бы предпочел что-то вроде этого:
value = "Unsafe string"
query = "SELECT * FROM some_table WHERE some_char_field = %s;" % \
READY_TO_USE_QUOTING_FUNCTION(value)
cursor.execute( query ) # value will be correctly quoted, too
Является ли такой низкий уровень READY_TO_USE_QUOTING_FUNCTION
ожидаемым по спецификации API Python DB (я не мог найти такую функциональность в PEP 249 document). Если нет, возможно, Psycopg2 предоставляет такую функцию? Если нет, возможно, Django предоставляет такую функцию? Я бы предпочел не писать такую функцию сам...
Ответы
Ответ 1
Хорошо, поэтому мне было любопытно, и я пошел посмотреть на источник psycopg2. Оказывается, мне не нужно было идти дальше, чем папка с примерами:)
И да, это psycopg2-specific. В принципе, если вы просто хотите указать строку, вы бы это сделали:
from psycopg2.extensions import adapt
print adapt("Hello World'; DROP DATABASE World;")
Но то, что вы, вероятно, хотите сделать, это написать и зарегистрировать свой собственный адаптер;
В папке примеров psycopg2 вы найдете файл 'myfirstrecipe.py', где приведен пример того, как отличать и цитировать определенный тип особым образом.
Если у вас есть объекты для материала, который вы хотите сделать, вы можете просто создать адаптер, соответствующий протоколу IPsycopgSQLQuote (см. pydocs для примера myfirstrecipe.py... фактически, что единственная ссылка, которую я могу найти к этому имени), который цитирует ваш объект и затем регистрирует его так:
from psycopg2.extensions import register_adapter
register_adapter(mytype, myadapter)
Кроме того, интересны другие примеры; особенно 'dialtone.py' и 'simple.py'.
Ответ 2
Я предполагаю, что вы ищете функцию mogrify.
Пример:
>>> cur.mogrify("INSERT INTO test (num, data) VALUES (%s, %s)", (42, 'bar'))
"INSERT INTO test (num, data) VALUES (42, E'bar')"
Ответ 3
Вы должны стараться избегать делать свои собственные цитаты. Они не только будут специфичны для БД, как указали люди, но недостатки в цитировании являются источником ошибок SQL-инъекций.
Если вы не хотите обойти запросы и значения отдельно, перейдите по списку параметров:
def make_my_query():
# ...
return sql, (value1, value2)
def do_it():
query = make_my_query()
cursor.execute(*query)
(Вероятно, у меня есть синтаксис cursor.execute). Дело здесь в том, что только потому, что cursor.execute принимает несколько аргументов, это не значит, что вы должны обрабатывать их все отдельно. Вы можете рассматривать их как один список.
Ответ 4
Я не думаю, что вы даете достаточные аргументы в пользу вашего избегания, чтобы сделать это правильно. Пожалуйста, используйте APi так, как он разработан, и не пытайтесь так сильно сделать ваш код менее читаемым для следующего парня и более хрупким.
Ответ 5
Это зависит от БД. В случае MySQLdb, например, класс connection
имеет метод literal
, который преобразует значение в правильное экранированное представление для передачи в MySQL (что используется cursor.execute
).
Я думаю, Postgres имеет что-то подобное, но я не думаю, что есть функция, чтобы избежать значений как часть спецификации DB API 2.0.
Ответ 6
Это будет зависимым от базы данных (iirc, mysql разрешает \
как escape-символ, а что-то вроде oracle ожидает, что кавычки будут удвоены: 'my '' quoted string'
).
Кто-то исправит меня, если я ошибаюсь, но метод двойного цитирования является стандартным методом.
Возможно, стоит посмотреть, что делают другие библиотеки абстракции db (sqlalchemy, cx_Oracle, sqlite и т.д.).
Мне нужно спросить - почему вы хотите встраивать значения, а не связывать их?
Ответ 7
Ваш фрагмент кода получится именно так, согласно документации по расширению psycopg
from psycopg2.extensions import adapt
value = "Unsafe string"
query = "SELECT * FROM some_table WHERE some_char_field = %s;" % \
adapt(value).getquoted()
cursor.execute( query ) # value will be correctly quoted, too
Функция getquoted
возвращает value
как строку с кавычками и экранированием, поэтому вы также можете пойти: "SELECT * FROM some_table WHERE some_char_field = " + adapt(value).getquoted()
.
Ответ 8
PyPika - еще один хороший вариант для построения операторов SQL. Пример использования (на основе примера на домашней странице проекта):
>>> from pypika import Order, Query
>>> Query.from_('customers').select('id', 'fname', 'lname', 'phone').orderby('id', order=Order.desc)
SELECT "id","fname","lname","phone" FROM "customers" ORDER BY "id" DESC
Ответ 9
Если вы используете django, вы можете использовать функцию кавычек, которая автоматически адаптируется к текущей конфигурации СУБД:
from django.db import backend
my_quoted_variable = backend.DatabaseOperations().quote_name(myvar)
Ответ 10
import re
def db_quote(s):
return "\"" + re.escape(s) + "\""
может выполнять задание простого цитирования, которое работает, по крайней мере, с MySQL. Нам действительно нужна функция cursor.format(), которая будет работать как cursor.execute(), за исключением того, что она вернет результирующий запрос вместо его выполнения. Иногда вы не хотите, чтобы запрос выполнялся довольно давно - например, вы можете сначала его зарегистрировать или распечатать для отладки, прежде чем продолжить его.