Как явно заключить строковое значение в кавычки (Python DB API / Psycopg2) - PullRequest
27 голосов
/ 21 ноября 2008

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

Ответы [ 10 ]

29 голосов
/ 23 ноября 2008

Хорошо, мне было любопытно, я пошел и посмотрел на источник 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)

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

15 голосов
/ 06 июля 2014

Полагаю, вам нужна функция mogrify .

Пример:

>>> cur.mogrify("INSERT INTO test (num, data) VALUES (%s, %s)", (42, 'bar'))
"INSERT INTO test (num, data) VALUES (42, E'bar')"
2 голосов
/ 22 ноября 2008

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

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

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

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

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

1 голос
/ 23 ноября 2008

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

0 голосов
/ 29 января 2019

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
0 голосов
/ 08 февраля 2013
import re

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

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

0 голосов
/ 13 декабря 2012

Ваш фрагмент кода будет выглядеть следующим образом, в соответствии с документами по расширению 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().

0 голосов
/ 23 ноября 2008

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

from django.db import backend
my_quoted_variable = backend.DatabaseOperations().quote_name(myvar)
0 голосов
/ 21 ноября 2008

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

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

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

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

0 голосов
/ 21 ноября 2008

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

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

...