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) と同様の質問ですが、答えが見つかりませんでした。)
ええ、これは本当の混乱です。 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に準拠していません。はぁ...
この問題を別の角度から見ることもできます。なんでしょう? any string引数の場合、引数に「%」を追加してLIKEを実行するクエリが必要です。関数やpsycopg2拡張機能に頼らずに、それを表現する良い方法は次のとおりです。
sql = "... WHERE ... LIKE %(myvalue)s||'%'"
cursor.execute(sql, { 'myvalue': '20% of all'})
パーセント文字をエスケープする代わりに、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に関して水を濁さないでください。
上記のすべてが本当に必要なのだろうか。私はpsycopg2を使用していて、単に使用することができました:
data_dict['like'] = psycopg2.Binary('%'+ match_string +'%')
cursor.execute("SELECT * FROM some_table WHERE description ILIKE %(like)s;", data_dict)
[〜#〜] like [〜#〜]オペランドで%
を使用することにより、%%
をエスケープすることができました。
sql_query = "select * from mytable where website like '%%.com'"
cursor.fetchall(sql_query)
上記のコードにいくつかの変更を加えて、次のことを行いました。
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)
私はより良いハックを見つけました。検索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+'%'))
プリペアドステートメントを使用している場合、SQLインジェクションを防ぐために、入力は''
でラップされます。これは素晴らしいですが、入力とSQLの連結も防ぎます。
これを回避する最善かつ最も安全な方法は、入力の一部として%
(s)を渡すことです。
cursor.execute('SELECT * FROM goats WHERE name LIKE %(name)s', { 'name': '%{}%'.format(name)})
これまでに組み込み関数を見つけることができなかったので、私が書いたものは非常に単純です。
def escape_sql_like(s):
return s.replace('\\', '\\\\').replace('%', '\\%').replace('_', '\\_')
Like
サブクラス化str
および アダプターを登録 を作成して、構文のように正しく変換することができます(たとえば、escape_sql_like()
を使用)。あなたが書いた)。