如何显式引用字符串值(Python DB API / Psycopg2)

时间:2008-11-21 19:47:12

标签: python sql django psycopg2

由于某些原因,我想明确引用字符串值(成为构造的SQL查询的一部分),而不是等待cursor.execute方法对其第二个参数的内容执行的隐式引用。 / p>

通过“隐含引用”我的意思是:

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

Python DB API规范是否期望这样的低级READY_TO_USE_QUOTING_FUNCTION(我在PEP 249文档中找不到这样的功能)。如果没有,也许Psycopg2提供这样的功能?如果没有,也许Django提供这样的功能?我不想自己写这样的功能......

10 个答案:

答案 0 :(得分:29)

好的,所以我很好奇,然后去看了一下psycopg2的来源。事实证明,我没有比示例文件夹更进一步:)

是的,这是psycopg2特有的。基本上,如果你只想引用一个字符串,你可以这样做:

from psycopg2.extensions import adapt

print adapt("Hello World'; DROP DATABASE World;")

但您可能想要做的是编写并注册自己的适配器;

在psycopg2的examples文件夹中,您可以找到文件'myfirstrecipe.py',其中有一个示例,说明如何以特殊方式转换和引用特定类型。

如果您有想要做的事情的对象,您可以创建一个符合'IPsycopgSQLQuote'协议的适配器(请参阅myfirstrecipe.py示例的pydocs ...实际上这是我能找到的唯一参考引用你的对象,然后像这样注册它:

from psycopg2.extensions import register_adapter

register_adapter(mytype, myadapter)

此外,其他例子很有趣; ESP。 'dialtone.py''simple.py'

答案 1 :(得分:15)

我猜您正在寻找mogrify功能。

示例:

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

答案 2 :(得分:2)

你应该尽量避免做自己的引用。正如人们所指出的那样,它不仅是特定于数据库的,而且引用中的缺陷也是SQL注入错误的根源。

如果您不想单独传递查询和值,请传递参数列表:

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

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

(我可能有cursor.execute的语法错误)这里的要点是因为cursor.execute需要多个参数,这并不意味着你必须单独处理它们。您可以将它们作为一个列表来处理。

答案 3 :(得分:1)

我认为你没有给出任何充分的理由来避免你做正确的方法。请按照设计使用APi,不要那么努力让下一个人的代码更不易读,更脆弱。

答案 4 :(得分:0)

这将取决于数据库。例如,在MySQLdb的情况下,connection类具有literal方法,该方法将值转换为正确的转义表示以传递给MySQL(这是cursor.execute使用的)。 / p>

我认为Postgres有类似的东西,但我不认为有一个函数可以转义值作为DB API 2.0规范的一部分。

答案 5 :(得分:0)

这将依赖于数据库(iirc,mysql允许\作为转义字符,而像oracle这样的东西希望引号加倍:'my '' quoted string')。

如果我错了,有人会纠正我,但双引号方法是标准方法。

可能值得一看其他数据库抽象库的作用(sqlalchemy,cx_Oracle,sqlite等)。

我必须问 - 为什么要内联值而不是绑定它们?

答案 6 :(得分:0)

根据psycopg extension 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()

答案 7 :(得分:0)

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

答案 8 :(得分:-1)

如果您使用django,您可能希望使用自动适应当前配置的DBMS的引用功能:

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

答案 9 :(得分:-1)

import re

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

可以完成简单引用的工作,至少可以使用MySQL。我们真正需要的是cursor.format()函数,它将像cursor.execute()一样工作,除了它将返回结果查询而不是执行它。有时您不希望查询执行得很好 - 例如,您可能希望先将其记录下来,或者在进行调试之前将其打印出来进行调试。