用psycopg2转义Postgres中的SQL "LIKE"值

56 投票
13 回答
41511 浏览
提问于 2025-04-15 18:17

psycopg2有没有一个函数可以用来处理Postgres中LIKE操作符的值呢?

比如说,我想匹配以“20% of all”开头的字符串,所以我想写类似这样的代码:

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

这里有没有现成的escape_sql_like函数可以用呢?

(这个问题和如何明确引用字符串值(Python DB API/Psycopg2)的问题类似,但我在那儿没找到答案。)

13 个回答

8

如果你在使用预处理语句,那么输入的数据会被包裹在''中,这样可以防止SQL注入攻击。这是个好方法,但也会限制你把输入和SQL语句直接拼接在一起。

解决这个问题最好的方法就是把%(s)作为输入的一部分传入。

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

我可以通过在LIKE操作符中使用%%来转义%

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

是的,这真是一团糟。MySQL和PostgreSQL默认都使用反斜杠来转义字符。如果你还要用反斜杠再次转义字符串,而不是使用参数化,这就会变得非常麻烦。而且根据ANSI SQL:1992的规定,默认情况下没有额外的转义字符,所以也就没有办法直接包含字面上的%_

我猜如果你关闭了反斜杠转义(这本身就不符合ANSI SQL),使用MySQL中的NO_BACKSLASH_ESCAPE sql_mode或者PostgreSQL中的standard_conforming_strings配置,简单的反斜杠替换方法也会出问题(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的数据库中都能工作(当然,不同的数据库模块的参数样式会有所不同)。

不过在MS SQL Server/Sybase中可能仍然会有问题,因为它们似乎也允许在LIKE表达式中使用[a-z]风格的字符组。在这种情况下,你还需要用.replace('[', '=[')来转义字面上的[字符。不过根据ANSI SQL,转义一个不需要转义的字符是无效的!(真让人抓狂!)所以虽然在真实的数据库管理系统中可能仍然能工作,但你依然不符合ANSI标准。唉……

撰写回答