Escape SQL "LIKE" valeur pour Postgres avec psycopg2
Ne psycopg2 ont une fonction pour échapper à la valeur d'un COMME opérande pour Postgres?
par exemple, je voudrais peut-être faire correspondre les chaînes qui commencent par la chaîne "20% de tous", donc je veux écrire quelque chose comme ceci:
sql = '... WHERE ... LIKE %(myvalue)s'
cursor.fetchall(sql, { 'myvalue': escape_sql_like('20% of all') + '%' }
y a-t-il une fonction existante escape_sql_like que je pourrais brancher ici?
(question similaire à comment citer une chaîne valeur explicitement (Python DB API / Psycopg2) , mais je n'ai pas trouvé de réponse.)
8 réponses
Oui, c'est un vrai bordel. MySQL et PostgreSQL utilisent tous les deux backslash-escapes par défaut. Il s'agit d'une douleur terrible si vous êtes également échapper à la chaîne de nouveau avec des backslashs au lieu d'utiliser la paramétrisation, et il est également incorrect selon ANSI SQL:1992, qui dit qu'il n'y a pas par défaut de caractères d'évasion supplémentaires au-dessus de la chaîne de s'échapper normale, et donc aucun moyen d'inclure un littéral %
ou _
.
je suppose que le simple la méthode backslash-replace tourne aussi mal si vous désactivez le backslash-escapes (qui sont eux-mêmes non conformes avec ANSI SQL), en utilisant NO_BACKSLASH_ESCAPE
sql_mode dans MySQL ou standard_conforming_strings
conf dans PostgreSQL (ce que les devs de PostgreSQL ont menacé de faire pour quelques versions maintenant).
la seule vraie solution est d'utiliser la syntaxe peu connue LIKE...ESCAPE
pour spécifier un caractère d'échappement explicite pour le LIKE
-pattern. Ceci est utilisé à la place du backslash-escape dans MySQL et PostgreSQL, les rendant conformes à ce que tout le monde fait et donnant une façon garantie d'inclure les caractères hors bande. Par exemple, avec le panneau =
comme moyen d'évasion:
# 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+'%'))
cela fonctionne sur les bases de données PostgreSQL, MySQL, et ANSI SQL-compliant (modulo le paramstyle bien sûr qui change sur les différents modules db).
il peut encore y avoir un problème avec MS SQL Server/Sybase, qui apparemment permet également les groupes de caractères [a-z]
dans les expressions LIKE
. Dans ce cas, vous voudriez aussi échapper au caractère littéral [
avec .replace('[', '=[')
. Cependant, selon ANSI SQL échapper à un caractère qui n'a pas besoin d'échapper est invalide! (Argh!) Donc, même si cela fonctionne probablement toujours à travers les vrais DBMSs, vous ne seriez toujours pas conforme à L'ANSI. soupir...
vous pouvez également regarder ce problème d'un angle différent. Que voulez-vous? Vous voulez une requête qui pour n'importe quel" argument de chaîne 151930920 " exécute un comme en ajoutant un '%' à l'argument. Une bonne façon d'exprimer que, sans recourir aux fonctions et extensions psycopg2 pourrait être:
sql = "... WHERE ... LIKE %(myvalue)s||'%'"
cursor.execute(sql, { 'myvalue': '20% of all'})
au lieu d'échapper au caractère de pourcentage, vous pouvez utiliser L'implémentation regex de PostgreSQL.
par exemple, la requête suivante contre les catalogues système fournira une liste de requêtes actives qui ne sont pas du sous-système de démarrage automatique:
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;
puisque cette syntaxe de requête n'utilise pas le mot-clé "J'aime", vous êtes capable de faire ce que vous voulez... et ne salissez pas les eaux par rapport à python et psycopg2.
je me demande si tout ce qui précède est vraiment nécessaire. J'utilise psycopg2 et j'ai simplement pu utiliser:
data_dict['like'] = psycopg2.Binary('%'+ match_string +'%')
cursor.execute("SELECT * FROM some_table WHERE description ILIKE %(like)s;", data_dict)
n'ayant pas encore trouvé de fonction intégrée, celle que j'ai écrite est assez simple:
def escape_sql_like(s):
return s.replace('\', '\\').replace('%', '\%').replace('_', '\_')
vous pouvez créer un Like
class subclassing str
et enregistrer un adaptateur pour elle pour le faire converti dans la syntaxe à droite comme (par exemple en utilisant le escape_sql_like()
vous avez écrit).
j'ai fait quelques modifications au code ci-dessus pour faire ce qui suit:
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)
j'ai trouvé un meilleur piratage. Il suffit d'ajouter ' % ' à votre recherche 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+'%'))