8

PDO を使用してストアド プロシージャを呼び出そうとしていますが、結果を取得しようとすると次のエラーが発生します。

警告: パケットの順序が正しくありません。期待される 1 受信 16. パケット サイズ = 163

ストアド プロシージャは、一時テーブルから選択する前に閉じる 2 つのカーソルを使用しています。MySQLでSPを直接呼び出して結果を確認できるため、これが問題であると思われます。また、php_pdo_mysql.dll に移行する前に php_mysql 拡張機能を使用した場合、この SP で問題が発生したことはありません。PDO を使用して、PHP で INPUT パラメータを含む他の単純なストアド プロシージャを呼び出すこともでき、エラーなしで結果を取得できます。

エラーを返すコードは次のとおりです。

$db = new PDO('mysql:host='.__DB_HOST__.';dbname='.__DB_NAME__.';charset=utf8', __DB_USER__, __DB_PASS__);
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);

/* DOES NOT WORK */
$queryResult = $db->prepare("CALL GetResults(:siteId,null)");
$siteId = 19;
$queryResult->bindValue(':siteId', $siteId, PDO::PARAM_INT);
$queryResult->execute();
$result = $queryResult->fetchAll(PDO::FETCH_ASSOC); // returns packets out of order warning
print_r($result);

このコードは Try/Catch ブロックにあり、例外はスローされていません。実際、PHP はこれをブラウザで警告として表示しています。

私のストアド プロシージャの署名は次のようになります。

CREATE DEFINER=`root`@`localhost` 
PROCEDURE `GetResults`(IN siteIdParam INT(11), IN siteSearchText VARCHAR(45))

また、パラメーターの 1 つとしてnullを渡すことに問題があるかどうかもわかりません。最初のパラメーターがnullを渡す場合もあれば、2 番目のパラメーターを渡す場合もあります。ただし、常に MySQL サーバー上で直接動作します。

bindParam と bindValue を試しましたが、同じ結果でした。SPを投稿することもできますが、やり過ぎかもしれません。

PDO 拡張機能から追加のログを有効にする方法はありますか?

アイデアや提案はありますか?さらに詳しい情報が必要な場合は、お知らせください。

注: PHP v5.5.4 と MySQL v5.6.14 を使用しています。

4

4 に答える 4

15

この問題を解決するためにコードの一部を分離しようと何時間も費やした後、ATTR_EMULATE_PREPARES フラグを true に設定するとエラーが消えたことに気付きました。

$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);

これにより、PDO は、MySQL によってネイティブにではなく、準備されたステートメントをエミュレートするように指示されます。私が読んだことによると、MySQL と PHP の最新バージョンを使用している場合は、通常、このフラグをオフにすることをお勧めします (デフォルトでは true です)。詳細については、このSO article を参照してください。

これは MySQL のバグだと思います (バージョン 5.6.17 までは問題がありました)。この特定の問題についてはあまり議論されていないので、他の誰かがトラブルシューティングの時間を節約できることを願っています. この問題はこの MySQL バグ ページでも説明されていますが、投稿された解決策は私の状況では役に立ちませんでした。

于 2014-05-19T15:31:35.153 に答える
3

PDO::ATTR_EMULATE_PREPARES 属性を変更しようとするとすぐに、同じ問題に遭遇しました。

同じ PDO 接続で2 番目の準備済みステートメントを呼び出そうとすると、説明した内容を確実に再現できます。このようなもの:

$db = new PDO('mysql:host='.__DB_HOST__.';dbname='.__DB_NAME__.';charset=utf8', __DB_USER__, __DB_PASS__);
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);

/* DOES NOT WORK */
$queryResult = $db->prepare("CALL GetResults(:siteId,null)");
$siteId = 19;
$queryResult->bindValue(':siteId', $siteId, PDO::PARAM_INT);
$queryResult->execute();
$result = $queryResult->fetchAll(PDO::FETCH_ASSOC); // works fine
print_r($result); 

// Call a second stored procedure
$secondCall = $db->prepare("CALL GetOtherResults()");
$secondCall->execute();
$secondResult = $secondCall->fetchAll(PDO::FETCH_ASSOC); // returns packets out of order
print_r($secondResult);

あなたの例は1つの呼び出ししか示していませんが、このエラーをトリガーするには、ストアドプロシージャ呼び出しが同じファイルにある必要はなく、同じPDO接続を共有するだけです。以下の私のアドバイスがまだ役立つことを願っています。

ストアド プロシージャを実行すると、MySQL ドライバーは 2 つの行セットを返します。ネイティブ ドライバーでは、接続を再利用する前に両方を処理する必要があります。2 番目の行セットを準備して呼び出す前に、その内容を気にせず、カーソルを閉じても、2 番目の rowset に進む必要があります。このような:

...
$queryResult->execute();
$result = $queryResult->fetchAll(PDO::FETCH_ASSOC);
$queryResult->nextRowSet();
$queryResult->closeCursor();

// Now you can prepare your second statement
...

これら 2 つの追加の呼び出しを行うのを忘れた場合、PDO のエミュレートされた準備はより寛容なようです。

于 2015-05-16T11:21:01.153 に答える
2

@ http203これを使用してmysql 5.6.17でWAMPサーバーを使用して同じ問題に遭遇しました

$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);

問題を修正しますが、mysql.ini のクエリ制限を増やすこともお勧めします

max_allowed_packet = 1MB //Default

max_allowed_packet = 3MB

将来の問題を防ぐために、相対的な問題を抱えている人は、この参照記事を読んでください。 警告: パケットの順序が正しくありません。

于 2014-08-19T18:12:13.930 に答える