2010-01-21 6 views
25

Hat psycopg2 eine Funktion, um den Wert eines LIKE Operanden für Postgres zu umgehen?Escape SQL "LIKE" -Wert für Postgres mit psycopg2

Zum Beispiel kann ich will Strings übereinstimmen, die mit der Zeichenfolge „20% aller“ beginnen, so will ich, so etwas schreiben:

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

Gibt es eine bestehende escape_sql_like Funktion, die ich könnte hier einstecken?

(. Ähnliche Frage zu How to quote a string value explicitly (Python DB API/Psycopg2), aber ich dort keine Antwort finden konnte)

Antwort

0

gescheitert bisher eine eingebaute Funktion zu finden, die ich geschrieben habe, ist ziemlich einfach:

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

Sie haben vergessen '' 'und' '' –

+0

@JensTimmerman diese Funktion nur entkommen die gleichen Token, die normale Zeichenfolge zu verwenden, die auf das Ergebnis vor der Verwendung in einer Abfrage. Korrekte String-Escaping hängt von der Sessing 'standard_conforming_stings' und Das geht am besten mit dem Bibliothekscode – Jasen

23

Ja, das ist ein echtes Durcheinander. Sowohl MySQL als auch PostgreSQL verwenden standardmäßig Backslash-Escapes. Das ist ein schrecklicher Schmerz, wenn Sie die Zeichenkette auch wieder mit umgekehrten Schrägstrichen verlassen, anstatt die Parametrisierung zu verwenden, und sie ist auch falsch gemäß ANSI SQL: 1992, die besagt, dass standardmäßig keine zusätzlichen Escape-Zeichen oberhalb der normalen Zeichenkette vorhanden sind daher keine Möglichkeit, ein Literal % oder _ einzuschließen.

Ich würde davon ausgehen, die einfache Schrägstrich-ersetzen Methode geht auch falsch, wenn Sie die Backslash-Fluchten deaktivieren (die sich nicht konform mit ANSI SQL) sind, mit NO_BACKSLASH_ESCAPE sql_mode in MySQL oder standard_conforming_strings conf in PostgreSQL (welche die PostgreSQL Entwickler droht jetzt für ein paar Versionen zu tun).

Die einzige wirkliche Lösung ist die Verwendung der wenig bekannten LIKE...ESCAPE-Syntax, um ein explizites Escape-Zeichen für das LIKE-Muster anzugeben. Dies wird anstelle des Backslash-Escape in MySQL und PostgreSQL verwendet, sodass sie dem entsprechen, was alle anderen Benutzer tun, und eine garantierte Möglichkeit bietet, die Out-of-Band-Zeichen einzuschließen. Zum Beispiel mit dem = Zeichen als 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+'%')) 

Dies funktioniert auf PostgreSQL, MySQL und ANSI SQL-kompatiblen Datenbanken (Modulo der paramstyle natürlich, die auf verschiedenen db Module ändert).

Es kann immer noch ein Problem mit MS SQL Server/Sybase geben, das anscheinend auch [a-z] -Style-Zeichengruppen in LIKE Ausdrücken erlaubt. In diesem Fall möchten Sie auch das Literalzeichen mit .replace('[', '=[') zurückgeben. Nach ANSI SQL-Escaping ist jedoch ein Zeichen, das nicht zu entkommen ist, ungültig! (Argh!) Obwohl es wahrscheinlich immer noch über echte DBMS funktionieren wird, sind Sie immer noch nicht ANSI-konform. Seufzer ...

0

können Sie erstellen eine Like Klasse Subklassifizieren str und register an adapter for it, um es in die richtige Syntax wie umgewandelt (zum Beispiel unter Verwendung der escape_sql_like() Sie geschrieben haben).

+0

Eine interessante Idee, an die ich nicht gedacht hatte, aber Sie müssten die Escaped-String immer mit echten 'LIKE'-Operatoren (% oder _) kombinieren, sonst hätten Sie es genauso gut verwenden können '=' statt 'LIKE' Wenn Sie das tun, dann bin ich nicht sicher, was der Vorteil dieses Ansatzes gegenüber dem einfacheren Ansatz ist, einfach die Escape-Funktion aufzurufen – EMP

1

Ich frage mich, ob all das oben genannte wirklich benötigt wird.Ich verwende psycopg2 und war einfach in der Lage zu verwenden:

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

hat nicht für mich funktioniert – dfrankow

+4

Es kann noch einfacher : 'cursor.execute (" SELECT * FROM some_table WHERE Beschreibung LIKE% s; ", ['foobar%']); –

0

ich einige Änderungen an den Code oben gemacht folgend zu tun:

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

Sie auch bei diesem Problem aus einem anderen Blickwinkel betrachten kann. Was willst du? Sie wollen eine Abfrage, die für beliebige String-Argument führt ein Like durch Anhängen eines "%" an das Argument. Eine gute Möglichkeit, dass auszudrücken, ohne auf Funktionen zurückgreifen und psycopg2 Erweiterungen sein könnten:

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

die Zeichenfolge entsprechen würde s wie 2001 hatte den schlimmsten aller Terrorismus – Jasen

2

Statt das Prozentzeichen zu entkommen, Sie könnten stattdessen den Einsatz von PostgreSQL regex Implementierung machen.

Zum Beispiel die folgende Abfrage für den Systemkatalogen wird eine Liste der aktiven Abfragen bereitzustellen, die nicht aus dem autovacuuming Subsystems sind:

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; 

Da diese Abfragesyntax verwendet nicht das ‚LIKE‘ Stichwort, Sie können tun, was Sie wollen ... und nicht die Gewässer in Bezug auf Python und Psycopg2 trüben.