簡體   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