【问题标题】:Escape SQL "LIKE" value for Postgres with psycopg2使用 psycopg2 为 Postgres 转义 SQL“LIKE”值
【发布时间】:2011-01-07 13:14:24
【问题描述】:

psycopg2 是否具有转义 Postgres 的 LIKE 操作数的值的功能?

例如我可能想匹配以字符串“20% of all”开头的字符串,所以我想写这样的东西:

sql = '... WHERE ... LIKE %(myvalue)s'
cursor.fetchall(sql, { 'myvalue': escape_sql_like('20% of all') + '%' }

我可以在这里插入一个现有的 escape_sql_like 函数吗?

(与How to quote a string value explicitly (Python DB API/Psycopg2) 类似的问题,但我在那里找不到答案。)

【问题讨论】:

    标签: python postgresql psycopg2 python-db-api


    【解决方案1】:

    至今没找到内置函数,我写的很简单:

    def escape_sql_like(s):
        return s.replace('\\', '\\\\').replace('%', '\\%').replace('_', '\\_')
    

    【讨论】:

    • @JensTimmerman 此函数仅转义类似的标记,以便在查询中使用结果之前使用普通字符串转义结果。正确的字符串转义取决于 sessing standard_conforming_stings,因此最好使用库代码来完成。
    • 更简洁,re.sub(r'([%\\"\'_])', r'\\\1', s)
    【解决方案2】:

    是的,这真是一团糟。默认情况下,MySQL 和 PostgreSQL 都使用反斜杠转义符。如果您还使用反斜杠而不是使用参数化再次转义字符串,这将是一个可怕的痛苦,并且根据 ANSI SQL:1992 也是不正确的,它表示默认情况下在正常字符串转义之上没有额外的转义字符,并且因此无法包含文字 %_

    如果您在 MySQL 中使用 NO_BACKSLASH_ESCAPE sql_mode 或在 PostgreSQL 中使用 standard_conforming_strings conf 关闭反斜杠转义(它们本身不符合 ANSI SQL),我认为简单的反斜杠替换方法也会出错( PostgreSQL 开发人员现在威胁要为几个版本做这件事)。

    唯一真正的解决方案是使用鲜为人知的LIKE...ESCAPE 语法为LIKE-模式指定显式转义字符。这被用来代替 MySQL 和 PostgreSQL 中的反斜杠转义,使它们符合其他人的做法,并提供了包含带外字符的保证方式。例如使用= 符号作为转义符:

    # look for term anywhere within title
    term= term.replace('=', '==').replace('%', '=%').replace('_', '=_')
    sql= "SELECT * FROM things WHERE description LIKE %(like)s ESCAPE '='"
    cursor.execute(sql, dict(like= '%'+term+'%'))
    

    这适用于 PostgreSQL、MySQL 和 ANSI SQL 兼容的数据库(当然,以 paramstyle 为模,在不同的数据库模块上会发生变化)。

    MS SQL Server/Sybase 可能仍然存在问题,它显然也允许在LIKE 表达式中使用[a-z] 样式的字符组。在这种情况下,您还想用.replace('[', '=[') 转义文字[ 字符。但是根据 ANSI SQL 转义不需要转义的字符是无效的! (啊!)因此,尽管它可能仍然适用于真正的 DBMS,但您仍然不符合 ANSI。叹息……

    【讨论】:

    • standard_conforming_strings in postgres not 在类似查询中打破反斜杠转义。至少在 12.7 中不会。
    【解决方案3】:

    您可以创建一个 Like 类子类化 strregister an adapter for it 以将其转换为正确的类似语法(例如,使用您编写的 escape_sql_like())。

    【讨论】:

    • 一个我没有想到的有趣的想法,但是你总是需要将转义的字符串与真正的LIKE 运算符(% 或 _)结合起来,否则你还不如使用@987654326 @ 而不是 LIKE。如果您这样做,那么我不确定这种方法与仅调用转义函数的简单方法相比有什么好处。
    【解决方案4】:

    我想知道是否真的需要以上所有内容。我正在使用 psycopg2 并且可以简单地使用:

    data_dict['like'] = psycopg2.Binary('%'+ match_string +'%')
    cursor.execute("SELECT * FROM some_table WHERE description ILIKE %(like)s;", data_dict)
    

    【讨论】:

    • 它可以更简单:`cursor.execute("SELECT * FROM some_table WHERE description LIKE %s;", ['foobar%']);
    【解决方案5】:

    我对上面的代码进行了一些修改以执行以下操作:

    def escape_sql_like(SQL):
        return SQL.replace("'%", 'PERCENTLEFT').replace("%'", 'PERCENTRIGHT')
    
    def reescape_sql_like(SQL):
        return SQL.replace('PERCENTLEFT', "'%").replace('PERCENTRIGHT', "%'")
    
    SQL = "SELECT blah LIKE '%OUCH%' FROM blah_tbl ... "
    SQL = escape_sql_like(SQL)
    tmpData = (LastDate,)
    SQL = cur.mogrify(SQL, tmpData)
    SQL = reescape_sql_like(SQL)
    cur.execute(SQL)
    

    【讨论】:

      【解决方案6】:

      您也可以从不同的角度看待这个问题。你想要什么?您想要一个查询,对于 any 字符串参数通过在参数中附加一个“%”来执行 LIKE。表达这一点的好方法是:

      sql = "... WHERE ... LIKE %(myvalue)s||'%'"
      cursor.execute(sql, { 'myvalue': '20% of all'})
      

      【讨论】:

      • 会匹配像2001 had worst of all terrorism这样的字符串
      • 不,不会,因为参数中的% 会被引用。
      • 没有什么告诉 pscopg2 % 需要特殊处理。
      • 是绑定变量:默认引用。
      • 是的,这就是它不起作用的原因。你应该测试一下。
      【解决方案7】:

      您可以使用 PostgreSQL 的正则表达式实现,而不是转义百分比字符。

      例如,针对系统目录的以下查询将提供不是来自自动清理子系统的活动查询列表:

      SELECT procpid, current_query FROM pg_stat_activity
      WHERE (CURRENT_TIMESTAMP - query_start) >= '%s minute'::interval
      AND current_query !~ '^autovacuum' ORDER BY (CURRENT_TIMESTAMP - query_start) DESC;
      

      由于此查询语法不使用“LIKE”关键字,因此您可以做任何您想做的事……而且不会在 python 和 psycopg2 方面弄得一团糟。

      【讨论】:

        【解决方案8】:

        我发现了一个更好的 hack。只需将“%”附加到您的搜索 query_text。

        con, queryset_list = psycopg2.connect(**self.config), None
        cur = con.cursor(cursor_factory=RealDictCursor)
        query = "SELECT * "
        query += " FROM questions WHERE  body LIKE %s OR title LIKE %s  "
        query += " ORDER BY questions.created_at"
        cur.execute(query, ('%'+self.q+'%', '%'+self.q+'%'))
        

        【讨论】:

          【解决方案9】:

          我能够通过在 LIKE 操作数中使用 %% 来转义 %

          sql_query = "select * from mytable where website like '%%.com'"
          cursor.fetchall(sql_query)
          

          【讨论】:

          • 这应该是解决方案。有什么问题吗?
          • 也许这是 PostgreSQL 版本相关的?这个答案是在问题发布几年后发布的。
          • 我搬了天地来解决这个问题,但只有这个方法有效。非常感谢!
          【解决方案10】:

          如果您使用的是准备好的语句,那么输入将被包裹在'' 中以防止 sql 注入。这很棒,但也可以防止输入 + sql 连接。

          最好和最安全的解决方法是将%(s) 作为输入的一部分。

          cursor.execute('SELECT * FROM goats WHERE name LIKE %(name)s', { 'name': '%{}%'.format(name)})
          

          【讨论】:

            【解决方案11】:

            我认为使用 f-strings 会更简单、更易读。

            query = f'''SELECT * FROM table where column like '%%{my_value}%%' '''
            cursor.execute(query)
            

            【讨论】:

            • 永远不要这样做,这是不安全的。引入了 SQL 注入的风险。
            • 好吧,这只是这个问题中使用相同方法的其他示例。这与安全性无关,而是如何在查询中转义 %。如果您使用来自用户的原始输入或类似的东西,这只是一个安全风险。
            【解决方案12】:

            它只需要在它之前和之后连接双 %。使用“ilike”而不是“like”使其不区分大小写。

            query = """
                select 
                    * 
                from 
                    table 
                where 
                    text_field ilike '%%' || %(search_text)s || '%%'
            """
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2014-05-09
              • 1970-01-01
              • 2014-02-10
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多