MySQL は、ひとつのステートメントの文字列に、 複数のステートメントを埋め込むことをオプションでサポートしています。 しかし、そうするためには特別な操作が必要です。
複数のステートメント、 またはマルチクエリーは、 mysqli::multi_query() を使って実行する必要があります。 文字列に埋め込まれる個々のステートメントは、 セミコロンで区切ります。 実行されたステートメントによって返される全ての結果セットは、 取得されなければいけません。
MySQL サーバは、 結果セットを返すステートメントと、 返さないステートメントを、 ひとつの複数のステートメントに埋め込むことができます。
例1 複数のステートメント
<?php
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli = new mysqli("example.com", "user", "password", "database");
$mysqli->query("DROP TABLE IF EXISTS test");
$mysqli->query("CREATE TABLE test(id INT)");
$sql = "SELECT COUNT(*) AS _num FROM test;
INSERT INTO test(id) VALUES (1);
SELECT COUNT(*) AS _num FROM test; ";
$mysqli->multi_query($sql);
do {
if ($result = $mysqli->store_result()) {
var_dump($result->fetch_all(MYSQLI_ASSOC));
$result->free();
}
} while ($mysqli->next_result());
上の例の出力は以下となります。
array(1) { [0]=> array(1) { ["_num"]=> string(1) "0" } } array(1) { [0]=> array(1) { ["_num"]=> string(1) "1" } }
セキュリティ上の考慮
mysqli::query() や
mysqli::real_query() は、
サーバで複数のクエリを処理するための接続フラグを設定しません。
複数のステートメントを使うことで、
SQLインジェクション攻撃の被害を軽減するためには、
追加のAPI呼び出しが必要です。
攻撃者は、; DROP DATABASE mysql
や ; SELECT SLEEP(999)
のようなステートメントを追加しようとするかもしれません。
攻撃者がステートメントにSQLを追加することに成功したが、
mysqli::multi_query
がそれを使わなければ、
サーバは挿入された、有害なSQLステートメントを実行することはありません。
例2 SQLインジェクション
<?php
$mysqli = new mysqli("example.com", "user", "password", "database");
$result = $mysqli->query("SELECT 1; DROP TABLE mysql.user");
if (!$result) {
echo "Error executing query: (" . $mysqli->errno . ") " . $mysqli->error;
}
?>
上の例の出力は以下となります。
Error executing query: (1064) You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'DROP TABLE mysql.user' at line 1
プリペアドステートメント
プリペアドステートメントを複数のステートメントで使うことは、 サポートされていません。
参照