2010-01-21 20 views
25

¿Tiene psycopg2 una función para escapar del valor de LIKE operando para Postgres?Valor "LIKE" de escape SQL para Postgres con psycopg2

Por ejemplo es posible que quiera hacer coincidir cadenas que comienzan con la cadena "20% de todos", por lo que quiero escribir algo como esto:

sql = '... WHERE ... LIKE %(myvalue)s' 
cursor.fetchall(sql, { 'myvalue': escape_sql_like('20% of all') + '%' } 

¿Hay una función existente escape_sql_like que yo podría enchufar aquí?

(. Pregunta similar a How to quote a string value explicitly (Python DB API/Psycopg2), pero no pude encontrar una respuesta allí)

Respuesta

0

Habiendo fracasó en encontrar una función incorporada en la medida, la que yo escribí es bastante simple:

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

has olvidado "'" y' "' –

+0

@JensTimmerman esta función solo escapa a los tokens similares, para usar la cadena normal que escapa del resultado antes de usarlo en una consulta. El escape de cadena correcto depende del sessing' standard_conforming_stings' y por lo tanto, lo mejor es usar el código de la biblioteca. – Jasen

23

Sí, esto es un verdadero desastre. Tanto MySQL como PostgreSQL usan escaneos de barra invertida para esto de manera predeterminada. Esto es un dolor terrible si también está escapando de nuevo con cadenas en lugar de utilizar la parametrización, y también es incorrecto de acuerdo con ANSI SQL: 1992, que dice que de manera predeterminada no hay caracteres de escape adicionales sobre el escape de cadena normal, y por lo tanto, no hay manera de incluir un literal % o _.

que presumiría el método simple barra invertida-sustituir también va mal si se apaga la barra invertida se escapa (que son ellos mismos no cumplen con las normas ANSI SQL), utilizando NO_BACKSLASH_ESCAPE sql_mode en MySQL o standard_conforming_strings conf en PostgreSQL (que el PostgreSQL los desarrolladores han amenazado con hacer un par de versiones ahora).

La única solución real es utilizar la sintaxis LIKE...ESCAPE poco conocida para especificar un carácter de escape explícito para LIKE -pattern. Esto se usa en lugar del escape de barra invertida en MySQL y PostgreSQL, haciéndolos ajustarse a lo que hacen todos los demás y dando una forma garantizada de incluir los caracteres fuera de banda. Por ejemplo con el signo = como un escape:

# 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+'%')) 

Esto funciona en PostgreSQL, MySQL, y bases de datos SQL-compatible con ANSI (módulo el paramstyle por supuesto que cambia en diferentes módulos dB).

Todavía puede haber un problema con MS SQL Server/Sybase, que aparentemente también permite [a-z] -style grupos de caracteres en expresiones LIKE. En este caso, también desearía escapar del carácter literal [ con .replace('[', '=['). Sin embargo, según ANSI SQL escapando, un personaje que no necesita escaparse no es válido. (Argh!) Entonces, aunque probablemente funcione en DBMS reales, aún no cumplirás con ANSI. suspiro ...

0

Puede crear una subclasificación Like clase str y register an adapter for it tenerlo convierte en la derecha como la sintaxis (por ejemplo, utilizando el escape_sql_like() que escribió).

+0

Una idea interesante que no había pensado, pero invariablemente necesitaría combinar la cadena escapada con operadores 'LIKE' reales (% o _); de lo contrario, podría haber usado '=' en lugar de 'LIKE'. Si haces eso, entonces no estoy seguro de cuál es el beneficio de este enfoque sobre el enfoque más simple de simplemente llamar a la función de escape. – EMP

1

Me pregunto si todo lo anterior es realmente necesario.Estoy usando psycopg2 y simplemente era capaz de usar:

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

no funcionó para mí – dfrankow

+4

Puede ser incluso más fácil : 'cursor.execute (" SELECT * FROM some_table WHERE description LIKE% s; ", ['foobar%']); –

0

Hice algunas modificaciones en el código anterior para hacer lo siguiente:

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

También puede ver el problema desde un ángulo diferente. ¿Qué deseas? Desea una consulta que para cualquier argumento de cadena ejecuta un LIKE al agregar un '%' al argumento. Una buena manera de expresar que, sin recurrir a las funciones y extensiones psycopg2 podría ser:

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

que coincidiría con la cadena s like '2001 tuvo el peor de todos terrorismo – Jasen

2

En lugar de escapar el carácter de porcentaje, en su lugar podría hacer uso de la aplicación de expresiones regulares de PostgreSQL.

Por ejemplo, la siguiente consulta en los catálogos del sistema proporcionará una lista de consultas activas que no son de la sub-sistema 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; 

Desde esta sintaxis de consulta no utiliza el 'como' palabra clave, puedes hacer lo que quieras ... y no ensuciar las aguas con respecto a python y psycopg2.