2010-01-21 6 views
25

Est-ce que psycopg2 a une fonction pour échapper la valeur d'un LIKE opérande pour Postgres?Escape SQL "LIKE" valeur pour Postgres avec psycopg2

Par exemple je veux 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 at-il une fonction escape_sql_like existante que je pourrait brancher ici?

(. Question similaire à How to quote a string value explicitly (Python DB API/Psycopg2), mais je ne pouvais pas trouver une réponse là-bas)

Répondre

0

Après avoir échoué à trouver une fonction à ce jour intégré, celui que j'ai écrit est assez simple:

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

Vous avez oublié "'" et' "' –

+0

@JensTimmerman Cette fonction n'échappe qu'aux jetons similaires, pour utiliser la chaîne normale qui s'échappe sur le résultat avant de l'utiliser dans une requête, la chaîne correcte s'échappe dépend du paramètre' standard_conforming_stings' et il est le mieux fait en utilisant le code de la bibliothèque. – Jasen

23

Oui, c'est un vrai bordel. MySQL et PostgreSQL utilisent des backslash-escape pour cela par défaut. C'est une douleur terrible si vous échappez à nouveau à la chaîne avec des antislashs au lieu d'utiliser le paramétrage, et c'est aussi incorrect selon ANSI SQL: 1992, qui dit qu'il n'y a pas de caractères d'échappement supplémentaires par défaut. donc aucun moyen d'inclure un littéral % ou _.

Je présume la méthode simple de antislash remplacer va aussi mal si vous désactivez la barre oblique inverse-échappements (qui sont eux-mêmes non conformes à la norme ANSI SQL), en utilisant NO_BACKSLASH_ESCAPE sql_mode dans MySQL ou standard_conforming_strings conf dans PostgreSQL (qui PostgreSQL les développeurs ont menacé de le faire pour quelques versions maintenant).

La seule solution réelle consiste à utiliser la syntaxe peu connue LIKE...ESCAPE pour spécifier un caractère d'échappement explicite pour le modèle LIKE. Ceci est utilisé à la place du backslash-escape dans MySQL et PostgreSQL, ce qui les rend conformes à ce que tout le monde fait et donne une garantie d'inclure les caractères hors-bande. Par exemple avec le signe = comme une é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 PostgreSQL, MySQL et ANSI bases de données SQL conformes (modulo le paramstyle bien sûr qui change sur les différents modules db).

Il se peut qu'il y ait toujours un problème avec MS SQL Server/Sybase, qui semble également autoriser les groupes de caractères [a-z] dans les expressions LIKE. Dans ce cas, vous voudriez également échapper le caractère littéral [ avec .replace('[', '=['). Cependant selon ANSI SQL s'échapper un caractère qui n'a pas besoin d'échapper est invalide! (Argh!) Donc, même si cela fonctionnera probablement encore sur de vrais SGBD, vous ne seriez toujours pas conforme à ANSI. soupir ...

0

Vous pouvez créer un sous-classement de la classe Likestr et register an adapter for it pour le convertir dans la droite comme la syntaxe (par exemple en utilisant la escape_sql_like() vous avez écrit).

+0

Une idée intéressante que je n'y avais pas pensé, mais vous toujours besoin de combiner la chaîne échappée avec de vrais 'opérateurs de LIKE' (en% ou _), sinon vous pourriez aussi bien avoir utilisé '' = au lieu de 'LIKE'. Si vous le faites alors je ne suis pas sûr de ce que l'avantage de cette approche est plus simple à l'approche de simplement appeler la fonction d'évacuation. – EMP

1

Je me demande si tout ce qui précède est vraiment nécessaire.J'utilise psycopg2 et était tout simplement capable d'utiliser:

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

ne fonctionne pas pour moi – dfrankow

+4

Il peut ve encore plus facile : 'cursor.execute ("SELECT * FROM some_table OU Description comme% s;", [ 'foobar%']); –

0

J'ai fait quelques modifications au code ci-dessus pour effectuer les opérations suivantes:

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) 
2

Vous pouvez également regarder ce problème sous un angle différent. Qu'est-ce que tu veux? Vous voulez une requête pour tout argument de chaîne exécute LIKE en ajoutant un « % » à l'argument. Une belle façon d'exprimer que, sans avoir recours à des fonctions et extensions psycopg2 pourrait être:

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

chaîne qui correspond s comme «2001 avait le pire de tous les terroristes» – Jasen

2

au lieu d'échapper au caractère pour cent, vous pouvez utiliser à la place de la mise en œuvre de regex de PostgreSQL.

Par exemple, la requête suivante sur les catalogues système fournira une liste de requêtes actives qui ne sont pas du sous-système autovacuum:

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; 

Depuis cette syntaxe de requête n'utilise pas le « LIKE » mot-clé, vous êtes capable de faire ce que vous voulez ... et non boueuse les eaux en ce qui concerne python et psycopg2.