繁体   English   中英

使用 psycopg2 为 Postgres 转义 SQL“LIKE”值

[英]Escape SQL "LIKE" value for Postgres with psycopg2

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

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

如果您关闭反斜杠转义(它们本身不符合 ANSI SQL),在 MySQL 中使用NO_BACKSLASH_ESCAPE sql_mode 或在 PostgreSQL 中使用standard_conforming_strings 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 的数据库(当然,以在不同 db 模块上更改的 paramstyle 为模)。

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

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

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

你也可以换个角度看这个问题。 你想要什么? 您需要一个查询,通过在参数后附加一个 '%' 来对任何字符串参数执行 LIKE。 一种表达这一点的好方法,不诉诸函数和 psycopg2 扩展可能是:

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

您可以使用 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 方面陷入困境。

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

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

如果您使用的是准备好的语句,则输入将包含在''以防止 sql 注入。 这很好,但也防止了 input + sql 连接。

解决此问题的最佳和最安全的方法是将% (s) 作为输入的一部分传入。

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

到目前为止还没有找到内置函数,我写的一个非常简单:

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

我找到了一个更好的黑客。 只需将“%”附加到您的搜索 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+'%'))

您可以创建一个Like类子类str为其注册一个适配器,以便将其转换为正确的 like 语法(例如,使用您编写的escape_sql_like() )。

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

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

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

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)

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM