文字列値を明示的に引用する方法(Python DB API / Psycopg2)
質問
何らかの理由で、 cursor.execute
メソッドによって実行される暗黙の引用を待つ代わりに、文字列値の明示的な引用(構築されたSQLクエリの一部になる)を行いたい2番目のパラメーター。
"暗黙の引用"つまり:
value = "Unsafe string"
query = "SELECT * FROM some_table WHERE some_char_field = %s;"
cursor.execute( query, (value,) ) # value will be correctly quoted
私はそのようなものを好むでしょう:
value = "Unsafe string"
query = "SELECT * FROM some_table WHERE some_char_field = %s;" % \
READY_TO_USE_QUOTING_FUNCTION(value)
cursor.execute( query ) # value will be correctly quoted, too
Python DB API仕様では、このような低レベルの READY_TO_USE_QUOTING_FUNCTION
が期待されています( PEP 249 ドキュメント)。そうでない場合、Psycopg2がそのような機能を提供していますか?そうでない場合、Djangoはそのような機能を提供しているのでしょうか?私はそのような関数を自分で書いたくない...
解決
わかりましたので、私は興味があり、psycopg2のソースを見に行きました。サンプルフォルダよりも先に行く必要はなかったことがわかりました:)
そして、はい、これはpsycopg2固有です。基本的に、文字列を引用したいだけなら、次のようにします:
from psycopg2.extensions import adapt
print adapt("Hello World'; DROP DATABASE World;")
しかし、おそらくあなたがしたいことは、あなた自身のアダプタを書いて登録することです;
psycopg2のサンプルフォルダーには、 'myfirstrecipe.py' 特定の型を特別な方法でキャストおよび引用する方法の例があります。
やりたいことのオブジェクトがある場合は、「IPsycopgSQLQuote」プロトコルに準拠するアダプターを作成するだけです(myfirstrecipe.py-exampleのpydocsを参照してください...実際に見つけることができる唯一のリファレンスです)その名前に)オブジェクトを引用し、次のように登録します:
from psycopg2.extensions import register_adapter
register_adapter(mytype, myadapter)
また、他の例も興味深いものです。特に 'dialtone.py' および 'simple.py' 。
他のヒント
mogrify 関数を探していると思います。
例:
>>> cur.mogrify("INSERT INTO test (num, data) VALUES (%s, %s)", (42, 'bar'))
"INSERT INTO test (num, data) VALUES (42, E'bar')"
独自の引用は避けてください。人々が指摘したようにDB固有であるだけでなく、引用の欠陥がSQLインジェクションのバグの原因です。
クエリと値を個別に渡したくない場合は、パラメータのリストを渡します:
def make_my_query():
# ...
return sql, (value1, value2)
def do_it():
query = make_my_query()
cursor.execute(*query)
(おそらく、cursor.executeの構文が間違っています)ここでのポイントは、cursor.executeが多数の引数を取るからといって、それらをすべて個別に処理する必要があるということではありません。それらを1つのリストとして扱うことができます。
この「正しい方法」を実行するための回避の背後にある十分な理由を与えるとは思わない。設計されたAPiを使用してください。コードを読みにくくして壊れやすくするために、一生懸命努力しないでください。
これはDBに依存します。たとえば、MySQLdbの場合、 connection
クラスには、値をMySQLに渡すための正しいエスケープ表現に変換する literal
メソッドがあります( cursor.execute
が使用します)。
Postgresには似たようなものがあると思いますが、DB API 2.0仕様の一部として値をエスケープする機能はないと思います。
これはデータベースに依存します(iirc、mysqlでは \
をエスケープ文字として使用できますが、oracleのようなものでは引用符が2倍になると予想されます: 'my' 'quoted string'
)。
間違っている場合は誰かが修正してくれますが、二重引用符で囲む方法が標準的な方法です。
他のdb抽象化ライブラリの機能(sqlalchemy、cx_Oracle、sqliteなど)を調べる価値があるかもしれません。
質問する必要があります-なぜ値をバインドするのではなくインライン化するのですか?
psycopg拡張ドキュメント によると、コードスニペットはこのようになりますp>
from psycopg2.extensions import adapt
value = "Unsafe string"
query = "SELECT * FROM some_table WHERE some_char_field = %s;" % \
adapt(value).getquoted()
cursor.execute( query ) # value will be correctly quoted, too
getquoted
関数は、引用符付きのエスケープ文字列として value
を返すので、次のようにすることもできます。" SELECT * FROM some_table WHERE some_char_field =" + apply(value).getquoted()
。
PyPika は、SQLステートメントを構築するための別の優れたオプションです。使用例(プロジェクトのホームページの例に基づく):
>>> from pypika import Order, Query
>>> Query.from_('customers').select('id', 'fname', 'lname', 'phone').orderby('id', order=Order.desc)
SELECT "id","fname","lname","phone" FROM "customers" ORDER BY "id" DESC
djangoを使用する場合は、現在構成されているDBMSに自動的に適応されるクォート機能を使用できます。
from django.db import backend
my_quoted_variable = backend.DatabaseOperations().quote_name(myvar)
import re
def db_quote(s):
return "\"" + re.escape(s) + "\""
少なくともMySQLで機能する単純な引用の仕事をすることができます。本当に必要なのは、cursor.execute()と同じように機能するcursor.format()関数ですが、実行する代わりに結果のクエリを返すことです。クエリをまだ実行したくない場合があります-たとえば、最初にログを記録するか、デバッグのために印刷してから実行することができます。