用psycopg2转义Postgres的SQL“LIKE”值

时间:2010-01-21 00:58:54

标签: python postgresql psycopg2 python-db-api

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)类似的问题,但我找不到答案。)

11 个答案:

答案 0 :(得分:27)

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

我认为如果你在MySQL中使用NO_BACKSLASH_ESCAPE sql_mode或standard_conforming_strings关闭反斜杠转义(它本身不符合ANSI SQL),简单的反斜杠替换方法也会出错PostgreSQL中的conf(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可能仍然存在问题,这显然也允许在[a-z]表达式中使用LIKE样式字符组。在这种情况下,您还希望使用[转义文字.replace('[', '=[')字符。但是根据ANSI SQL转义,不需要转义的字符无效! (唉!)虽然它可能仍然适用于真正的DBMS,但你仍然不符合ANSI标准。叹息...

答案 1 :(得分:4)

您还可以从不同角度查看此问题。你想要什么?您希望查询任何字符串参数通过在参数后附加'%'来执行LIKE。表达这一点的好方法,不需要求助于函数和psycopg2扩展,可以是:

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

答案 2 :(得分:2)

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

例如,针对系统目录的以下查询将提供不属于autovacuuming子系统的活动查询列表:

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进行混淆。

答案 3 :(得分:2)

通过使用 Like 操作数中的%,我可以逃脱%%

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

答案 4 :(得分:1)

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

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

答案 5 :(得分:0)

到目前为止找不到内置函数,我写的那个很简单:

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

答案 6 :(得分:0)

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

答案 7 :(得分:0)

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

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)

答案 8 :(得分:0)

我发现了一个更好的技巧。只需将“%”附加到您的搜索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 :(得分:0)

如果使用的是预准备语句,则输入将包装在''中,以防止sql注入。这很棒,但也可以防止输入+ sql串联。

最好的和最安全的方法是将%作为输入的一部分。

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

答案 10 :(得分:0)

我认为使用f字符串会更简单易读。

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