Как явно указать строковое значение (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 (я не смог найти такую функциональность в Пеп 249 документ). Если нет, возможно, Psycopg2 предоставляет такую функцию? Если нет, возможно, Django предоставляет такую функцию? Я бы предпочел не писать такую функцию сам...

9 ответов


хорошо, поэтому мне было любопытно, и я пошел и посмотрел на источник psycopg2. Оказывается, мне не нужно было идти дальше папки примеров:)

и да, это специфично для psycopg2. В принципе, если вы просто хотите процитировать строку, вы должны сделать это:

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)

кроме того, другие примеры интересны; esp. 'dialtone.py' и 'simple.py'.


Я думаю, вы ищете однако существует.

пример:

>>> cur.mogrify("INSERT INTO test (num, data) VALUES (%s, %s)", (42, 'bar'))
"INSERT INTO test (num, data) VALUES (42, E'bar')"

вы должны стараться избегать делать свои собственные цитаты. Мало того, что он будет специфичен для БД, как указывали люди, но недостатки в цитировании являются источником ошибок SQL-инъекций.

Если вы не хотите передавать запросы и значения отдельно, то передайте список параметров:

def make_my_query():
    # ...
    return sql, (value1, value2)

def do_it():
    query = make_my_query()
    cursor.execute(*query)

(У меня, вероятно, есть синтаксис курсора.execute wrong) дело здесь в том, что просто потому, что курсор.execute принимает несколько аргументов, что не означает, что вам нужно обрабатывать все по отдельности. Вы можете обращаться с ними как с одним списком.


Я не думаю, что вы даете какие-либо достаточные основания для вашего уклонения, чтобы сделать это правильно. Пожалуйста, используйте APi, как он разработан, и не пытайтесь сделать свой код менее читаемым для следующего парня и более хрупким.


Это будет зависеть от DB. В случае MySQLdb, например,connection класс literal метод, который преобразует значение в правильное экранированное представление для передачи MySQL (вот что cursor.execute использует).

Я предполагаю, что Postgres имеет что-то подобное, но я не думаю, что есть функция, чтобы избежать значений как часть спецификации DB API 2.0.


это будет зависеть от базы данных (iirc, mysql позволяет \ как escape-символ, в то время как что-то вроде oracle ожидает, что котировки будут удвоены: 'my '' quoted string').

кто-то поправит меня, если я ошибаюсь, но метод двойного цитирования является стандартным методом.

возможно, стоит посмотреть, что делают другие библиотеки абстракции БД (sqlalchemy, cx_Oracle, sqlite и т. д.).

Я должен спросить-почему вы хотите встроить значения вместо того, чтобы связывать их?


ваш фрагмент кода будет выглядеть примерно так, согласно psycopg расширение docs

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() .


Если вы используете django, вы можете использовать функцию цитирования, которая автоматически адаптируется к настроенной СУБД:

from django.db import backend
my_quoted_variable = backend.DatabaseOperations().quote_name(myvar)

import re

def db_quote(s):
  return "\"" + re.escape(s) + "\""

может выполнять работу простого цитирования, которое работает по крайней мере с MySQL. Что нам действительно нужно, так это курсор.формат (функция), которая будет работать как курсор.execute (), за исключением того, что он вернет результирующий запрос вместо его выполнения. Бывают случаи, когда вы еще не хотите, чтобы запрос выполнялся - e.g вы можете сначала зарегистрировать его или распечатать для отладки, прежде чем приступить к нему.