ADO とプリペアドステートメント

最近、入力値検査の不備を突いたSQLインジェクションが取りざたされているのを見ていて、Windows上で、共通のAPIによるデータベースへのアクセスを可能にするActiveXコンポーネントADO(ActiveX Data Objects)では、対策を講じられるかどうか不安になって調べてみた。
どうやら、ADOはプリペアドステートメントを利用出来るらしいことが分かった。正確にはMSDNがパラメータクエリと呼んでいるものだけど。
まずは、ADODB.Commandオブジェクトを生成して、現在のコネクションをCommand#ActiveConnectionプロパティに束縛する。これにより、コマンドとコネクションが関連づけられる。

// connect to the database
conn = new ActiveXObject("ADODB.Connection");
conn.Open(...);
// create a command object
cmd = new ActiveXObject("ADODB.Command");
cmd.ActiveConnection = conn;

次はコマンド、具体的にはプリペアドステートメントを定義する。コマンドの種類を表すCommand#CommandTypeプロパティに、コマンドがSQLステートメントであることを表すadCmdText(1)を、コマンドの内容を表すCommand#CommandTextプロパティに、プリペアドステートメントを束縛する。

cmd.CommandType = adCmdText; // = 1;
cmd.CommandText = "SELECT * from someTable WHERE someItem = ?";

これだけでは、まだクエリを発行することは出来ない。プリペアドステートメントにあるパラメータマーカーと、コマンドが保持するパラメータコレクションの関連付けが行われていないからだ。Command#CreateParameterメソッドによってParameterオブジェクトを生成し、Command#Parameters#Appendメソッド で、パラメータコレクションにパラメータの追加を行う。

cmd.Parameters.Append(cmd.CreateParameter('someWChar', adWChar, 1, 1024));

最後に、コマンドがプリペアドステートメントであることを表明するために、Command#PreparedプロパティをTrueに設定すれば、準備完了だ。

cmd.Prepared = true;

ステートメントをプリコンパイルする必要がない場合は、Falseでも構わないようだ。大量のクエリを発行する場合は、Trueにしておいた方がいいと思う。
クエリを発行する際のパラメータ設定は以下のようにするようだ。コマンド実行はCommand#Executeメソッドを呼ぶ。

// same as: cmd.Parameters.Item('someWChar') = 'Hello, world!';
cmd('someWChar') = 'Hello, world!';
// execute the command
var recordSet = cmd.Execute();

ネイティブでは、パラメータクエリの発行にややこしいイディオムがあった記憶があるけど、こっちは比較的分かりやすいし、使いやすいなぁ。