2017-12-17 14 views
1

pyodbcを使用してデータベースに接続するアプリケーションがあります。異なるdbエンジンで実行することができます。今私はそれをFirebirdに適応しようとします。私は結合parametres処理するためにネイティブpyodbcの方法を使用することができます(sqliteのでテスト、Sybaseの)いくつかのDBエンジンのためのpyodbcをFirebirdに接続してバインドするパラメータ

cnxn = pyodbc.connect(connect_string, autocommit=True) 
cur = cnxn.cursor() 
sql = """DECLARE v_file_type_id INT; v_path varchar2(1024); v_md5 varchar2(32); 
     BEGIN 
     v_file_type_id := %d; 
     v_path := '%s'; 
     v_md5 := '%s'; 
     insert into files (file_type_id, path, md5) values (v_file_type_id, v_path, v_md5); 
     END;""" % (file_type_id, path, md5) 
cur.execute(sql) 
:Oracleの場合

cnxn = pyodbc.connect(connect_string, autocommit=True) 
cur = cnxn.cursor() 
cur.execute("insert into files (file_type_id, path, md5) values (?, ?, ?)", file_type_id, path, md5) 

は、私が実行されるように、いくつかの特別なSQLを準備しなければなりませんでした

ファースト・ウェイはFirebirdでは動作しません。私はエラー:pyodbc.IntegrityError: ('23000', '[23000] [ODBC Firebird Driver][Firebird]validation error for column FILE_TYPE_ID, value "*** null ***" (-625) (SQLExecDirectW)')を受け取ります。

私は専用のSQLを用意しようとしましたが、Oracleの場合と同じですが、いくつか問題があります。

cnxn = pyodbc.connect(connect_string, autocommit=True) 
cur = cnxn.cursor() 
sql = """set term^; 
     execute block 
     as 
     declare v_file_type_id int = %d; 
     declare v_path varchar(1024) = '%s'; 
     declare v_md5 varchar(32) = '%s'; 
     begin 
     insert into files (file_type_id, path, md5) values (:v_file_type_id, :v_path, :v_md5); 
     end 
     ^
     set term ; ^""" % (file_type_id, path, md5) 
cur.execute(sql) 

これは機能しません。今回のエラーはpyodbc.Error: ('HY000', '[HY000] [ODBC Firebird Driver][Firebird]Dynamic SQL Error\nSQL error code = -104\nToken unknown - line 1, column 5\nterm (-104) (SQLExecDirectW)')です。

私はどんな解決策も探しているので、その2つの方法のいずれかで実行することができます。パフォーマンスの側面も非常に重要です。アプリケーションは頻繁には実行されませんが、コードの一部で約15万回挿入すると、filesテーブルに挿入されます。最初のエラーは、他の言葉で、あなたはFILE_TYPE_IDと呼ばれるnot null列にnull値を挿入しているという事実によって引き起こされる

+1

ここではFirebirdが実行しようとしている実際のSQLを見てみましょう。実際のSQLが 'pyodbc'から実行されているのを見る方法はありません。また、SQLとパラメータを渡すときにはそうしてはいけません。 Firebirdでクエリのログを有効にして、エラーの原因となったSQLを含めることができますか? – FlipperPA

答えて

1

file_type_idあなたの変数はnullで、これは、カラム上の制約によって許可されていない、または何か他のものがあります間違っている。

私はPythonやそのDB-APIを使って何をしたので、それがしばらくあったが、私の知る限りリコールとして、あなたは、実行方法が間違っている:

cur.execute("insert into files (file_type_id, path, md5) values (?, ?, ?)", file_type_id, path, md5) 

はの使用を注意してください(であるべきタプル)

cur.execute("insert into files (file_type_id, path, md5) values (?, ?, ?)", (file_type_id, path, md5)) 

秒の誤差がset termが有効なのFirebird SQL文でないことに起因する、見るもfirebird procedural query throwing "token unknown" error at "SET TERM #;"

、文の終了を切り替えるためのクライアントツールに固有の文です

Executeブロックは匿名の 'stored'プロシージャであり、通常このようにプリペアドステートメントを実行するためには使用されません。実行ブロックを使用すると、文字列の書式を使用するためにSQLインジェクションを開くため、特に安全ではありません。実行ブロックのために、このフォームは 'normal'ステートメントを実行するよりも危険です。

PythonのFirebirdデータベースドライバは、FDBpyfirebirdsqlなので、ODBCを使う必要はありません。

+0

実際、pyodbcでは、パラメータ値をタプルとして渡す必要はありません(ref:[here](https://github.com/mkleehammer/pyodbc/wiki/Features-beyond-the-DB-API#passing-parameters) )。 –

+0

はい、最初のエラーは、それを許可しない列に 'null'値を入れることについて言います。しかし、私が 'file_type_id'が' null'ではないと信じて、 'cur.execute(...)'の直前に値を出力することができます。また、 'connect_string'をsqliteやSybaseデータベースに変更すると、同じコードが動作します。私はパラメータを渡すために両方の方法を試しました - 直接的なもの(上記のように)その場合、どちらも動作しません。最初と最後から 'set term'部分を削除しました。私は潜在的な危険を理解しています。ご協力いただきありがとうございます。 – Yama

+0

@Yamaええと、ODBCドライバのバグを示唆していますが、Firebird ODBCドライバ2.0.5を使用していますか? –

関連する問題