web-dev-qa-db-ja.com

OracleのC#パラメーター化クエリ-深刻で危険なバグ!

これは絶対的な遠吠えです。私は自分の目を信じることができず、C#の真のバグである場合、これを発見する前に誰も信じられなかったので、他の開発者コミュニティに私が間違っていることを教えてもらいます。この質問には、「DOH!」と言うことが含まれると確信しています。手のひらで頭を激しく叩きましたが、とにかくここに行きます...

テストのために、テーブルを作成しましたTest_1、次のようなスクリプトを使用:

CREATE TABLE TEST_1 (
  COLUMN1 NUMBER(12) NOT NULL,
  COLUMN2 VARCHAR2(20),
  COLUMN3 NUMBER(12))
TABLESPACE USERS
STORAGE (
  INITIAL 64K
  MAXEXTENTS UNLIMITED
)
LOGGING;

ここで、次のコードを実行します。

var conn = new OracleConnection("connectionblahblah");
conn.Open();
var cmd = conn.CreateCommand();
cmd.CommandText = 
  "insert into Test_1(Column1, Column2, Column3) " +
  "values(:Column1, :Column2, :Column3)";
var p = cmd.Parameters;
p.Add("Column1", 1);
p.Add("Column3", null);
p.Add("Column2", "record 1");
cmd.ExecuteNonQuery();

うわあ! ORA-01722エラーが発生します-「無効な番号」です!しかし、何が問題なのですか? Column1は数値であり、値は1なので、問題ありません。 Column2は文字列であり、Column3はnull許容列であるため、問題が発生することはありません。

今これのために座ってください...ここでの問題はそれですColumn3およびColumn2は、OracleParameterCollectionに追加された順序で置き換えられます。それらを切り替えて、プレスト!できます!

もちろん、これは次の明らかな実験につながります...次のようなパラメータを追加するためにコードのブロックを変更しましょう:

p.Add("Foo", 1);
p.Add("Bar", "record 1");
p.Add("hahahahahahaha", null);

あなたはそれがうまくいくと思いますか?よく推測してください-それします

私はここに座って絶対にびっくりしました。私は自分が見ているものを信じることができず、同様に、私の前に誰もこの行動を発見していないと信じることができません(Googleの適切な使用方法がわからない場合を除く)。

これは単なる煩わしさではなく、非常に危険です。同じデータ型の2つの列を転置するとどうなりますか?エラーも発生しませんでした。間違ったデータを間違った列に挿入しただけで、賢明ではありませんでした。

パラメータを間違った順序で追加しないように注意する以外に、回避策のアイデアはありますか?

37
Shaul Behr

これはバグではありませんが、OracleODP.Netのドキュメントに明示的に記載されています。 OracleCommandクラスでは、パラメータはデフォルトで位置によってバインドされます。名前でバインドする場合は、プロパティcmd.BindByName = true;を明示的に設定します。

Oracleドキュメントへの参照。 http://download.Oracle.com/docs/cd/E11882_01/win.112/e12249/OracleCommandClass.htm#i997666

45
softveda

Column2の前にcolumn3が追加されているというタイプミスですか?

コロン構文はバインド変数を意味するため、名前はPLSQLのBIND変数には関係ないため、送信順に入力されます。これは、column2値を「レコード1」として設定しようとしていることを意味します。これは、無効な数値エラーを説明します...

あなたは現在持っています:

p.Add("Column1", 1);
p.Add("Column3", null);
p.Add("Column2", "record 1");

...この変更で問題が解決するかどうかを確認してください。

p.Add("Column1", 1);
p.Add("Column2", "record 1");
p.Add("Column3", null);

名前付きパラメーターを機能させる?

名前付きパラメーターを機能させる方法を説明するには、C#の経験が豊富な人に任せる必要があります。しかし、コロンがOracleBIND変数として解釈されているように見えることを確認できてうれしいです。

4
OMG Ponies
p.Add(":Column1", 1);
p.Add(":Column2", "record 1");
p.Add(":Column3", null);

//注:Oracleデータクライアントによって認識されるパラメータ名に:を追加しました

0
Luke