代码之家  ›  专栏  ›  技术社区  ›  Dariusz Walczak

如何显式引用字符串值(python db api/psycopg2)

  •  27
  • Dariusz Walczak  · 技术社区  · 17 年前

    出于某些原因,我希望对字符串值进行显式引用(成为构造的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 回复  |  直到 7 年前
        1
  •  29
  •   Daniel Serodio    14 年前

    好吧,所以我很好奇,去看看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)
    

    另外,其他例子也很有趣;特别是。 'dialtone.py' 'simple.py' .

        2
  •  15
  •   Beli    12 年前

    我猜你在找 mogrify 功能。

    例子:

    >>> cur.mogrify("INSERT INTO test (num, data) VALUES (%s, %s)", (42, 'bar'))
    "INSERT INTO test (num, data) VALUES (42, E'bar')"
    
        3
  •  2
  •   Ned Batchelder    17 年前

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

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

    def make_my_query():
        # ...
        return sql, (value1, value2)
    
    def do_it():
        query = make_my_query()
        cursor.execute(*query)
    

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

        4
  •  1
  •   community wiki ironfroggy    17 年前

    我认为你在回避的背后没有给出足够的理由来以正确的方式做到这一点。请按照设计的方式使用API,不要试图使代码对下一个人的可读性变差,更脆弱。

        5
  •  0
  •   davidavr    17 年前

    这将取决于数据库。例如,对于mysqldb, connection 类有 literal 方法,将值转换为正确的转义表示形式以传递给MySQL(这就是 cursor.execute 用途)。

    我想Postgres也有类似的功能,但我不认为有函数可以将值作为DBAPI2.0规范的一部分进行转义。

        6
  •  0
  •   Richard Levasseur    17 年前

    这将依赖于数据库(IIRC,MySQL允许 \ 作为一个转义字符,虽然像Oracle这样的字符希望引号加倍: 'my '' quoted string' )

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

    可能值得研究一下其他数据库抽象库的功能(sqlacalchemy、cx-oracle、sqlite等)。

    我不得不问——为什么要内联这些值而不是绑定它们?

        7
  •  0
  •   Roberto    13 年前

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

        8
  •  0
  •   asherbret    7 年前

    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
  •  -1
  •   vincent    17 年前

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

    from django.db import backend
    my_quoted_variable = backend.DatabaseOperations().quote_name(myvar)
    
        10
  •  -1
  •   Sasha Pachev    13 年前
    import re
    
    def db_quote(s):
      return "\"" + re.escape(s) + "\""
    

    可以做简单的报价工作,至少在MySQL中有效。不过,我们真正需要的是cursor.format()函数,它的工作方式与cursor.execute()类似,只是它将返回结果查询,而不是执行查询。有时您还不希望执行查询,例如,您可能希望先将其记录下来,或者在继续执行查询之前将其打印出来进行调试。