Как явно указать строковое значение (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(), за исключением того, что она вернет результирующий запрос вместо его выполнения. Иногда вы не хотите, чтобы запрос выполнялся довольно давно - например, вы можете сначала его зарегистрировать или распечатать для отладки, прежде чем продолжить его.