web-dev-qa-db-ja.com

psycopg2を使用してPostgresのSQL「LIKE」値をエスケープする

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) と同様の質問ですが、答えが見つかりませんでした。)

33
EMP

ええ、これは本当の混乱です。 MySQLとPostgreSQLはどちらも、デフォルトでこれに円記号エスケープを使用します。パラメータ化を使用する代わりにバックスラッシュを使用して文字列を再度エスケープする場合、これはひどい苦痛です。また、ANSI SQL:1992によると、通常の文字列エスケープの上に余分なエスケープ文字がないことを示しています。したがって、リテラル_%_または___を含める方法はありません。

MySQLの_NO_BACKSLASH_ESCAPE_ sql_modeまたはPostgreSQLの_standard_conforming_strings_ confを使用して、バックスラッシュエスケープ(それ自体はANSI SQLに準拠していません)をオフにすると、単純なバックスラッシュ置換メソッドも失敗すると思います。 (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式で_[a-z]_スタイルの文字グループも許可されるようです。この場合、リテラル_[_文字を.replace('[', '=[')でエスケープすることもできます。ただし、ANSI SQLによると、エスケープする必要のない文字をエスケープすることは無効です。 (ああ!)したがって、実際のDBMSでも機能する可能性がありますが、ANSIに準拠していません。はぁ...

29
bobince

この問題を別の角度から見ることもできます。なんでしょう? any string引数の場合、引数に「%」を追加してLIKEを実行するクエリが必要です。関数やpsycopg2拡張機能に頼らずに、それを表現する良い方法は次のとおりです。

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

パーセント文字をエスケープする代わりに、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に関して水を濁さないでください。

2
Brian

上記のすべてが本当に必要なのだろうか。私はpsycopg2を使用していて、単に使用することができました:

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

[〜#〜] like [〜#〜]オペランドで%を使用することにより、%%をエスケープすることができました。

sql_query = "select * from mytable where website like '%%.com'"
cursor.fetchall(sql_query)
1
pandasuser

上記のコードにいくつかの変更を加えて、次のことを行いました。

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)
0
Bob Turner

私はより良いハックを見つけました。検索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+'%'))
0
p8ul

プリペアドステートメントを使用している場合、SQLインジェクションを防ぐために、入力は''でラップされます。これは素晴らしいですが、入力とSQLの連結も防ぎます。

これを回避する最善かつ最も安全な方法は、入力の一部として%(s)を渡すことです。

cursor.execute('SELECT * FROM goats WHERE name LIKE %(name)s', { 'name': '%{}%'.format(name)})
0
sdc

これまでに組み込み関数を見つけることができなかったので、私が書いたものは非常に単純です。

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

Likeサブクラス化strおよび アダプターを登録 を作成して、構文のように正しく変換することができます(たとえば、escape_sql_like()を使用)。あなたが書いた)。

0
piro