web-dev-qa-db-ja.com

PDOエラーの原因他のバッファリングされていないクエリがアクティブな間はクエリを実行できませんか?

私は次のコードを持っています:

_$dbh = new PDO("mysql:Host=$Host;dbname=$dbname", $user, $pass);
$dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$dbh->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, true);
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

$stmt = $dbh->prepare("SELECT 1");
$stmt->execute();
$result = $stmt->fetch();

$stmt->execute();
$result = $stmt->fetch();

$stmt = $dbh->prepare("SELECT 1");
$stmt->execute();
$result = $stmt->fetch();
_

ただし、何らかの理由で、second preparedステートメントを実行すると次のエラーが発生します。

致命的なエラー:キャッチされていない例外 'PDOException'とメッセージ 'SQLSTATE [HY000]:一般エラー:2014他のバッファリングされていないクエリがアクティブな間はクエリを実行できません。 PDOStatement :: fetchAll()の使用を検討してください。または、コードがmysqlに対してのみ実行される場合は、PDO :: MYSQL_ATTR_USE_BUFFERED_QUERY属性を設定してクエリバッファリングを有効にすることができます。

このエラーの意味とその修正方法(unset($stmt);または$stmt->closeCursor();のいずれかを実行)を知っているので、私はそれを機能させる方法の解決策を探していません。私が理解していることから、これは通常、fetchの代わりにfetchAllを実行し、すべての結果をフェッチしていないことが原因です。ただし、この場合、結果は1つだけですであり、フェッチされています。また、最初に準備したステートメントを1回だけ実行しても、エラーは発生しません。これは、最初のステートメントが実行されたときにのみ発生しますtwice。また、_PDO::ATTR_EMULATE_PREPARES_がfalseの場合にのみ発生します。

だから私の質問は、この場合に上記のエラーが発生する原因は何ですか?これまでに実行した他のクエリと何ら変わらないようです。

私はこれを2つのUbuntu13.10サーバー、DebianとCentOSでテストしましたが、デフォルトのパッケージを使用するとすべて同じエラーが発生します。

編集:

Ryan Vincentのコメントに答えるために、私は完全なmysqli noobですが、以下にあるものは上記の例とほぼ同等であると思います。私が間違っている場合は私を訂正してください。ただし、エラーは発生しないため、PDOのみのエラーのように見えます。

_$mysqli = new mysqli($Host, $user, $pass, $dbname);
if ($mysqli->connect_errno) {
    die("Failed to connect to MySQL: (" . $mysqli->connect_errno . ") " . $mysqli->connect_error);
}

if (!($stmt = $mysqli->prepare("SELECT 1"))) {
     die("Prepare 1 failed: (" . $mysqli->errno . ") " . $mysqli->error);
}

if (!$stmt->execute()) {
    die("Execute 1 failed: (" . $stmt->errno . ") " . $stmt->error);
}
$stmt->store_result();
$stmt->bind_result($col1);
$stmt->fetch();

if (!$stmt->execute()) {
    die("Execute 2 failed: (" . $stmt->errno . ") " . $stmt->error);
}
$stmt->store_result();
$stmt->bind_result($col1);
$stmt->fetch();

if (!($stmt = $mysqli->prepare("SELECT 1"))) {
    // The following line is what fails in PDO
    die("Prepare 2 failed: (" . $mysqli->errno . ") " . $mysqli->error);
}

if (!$stmt->execute()) {
    die("Execute 3 failed: (" . $stmt->errno . ") " . $stmt->error);
}
$stmt->store_result();
$stmt->bind_result($col1);
$stmt->fetch();
_
13
Mike

奇妙なことに、Ubuntuが提供するPHPパッケージは Mysqlネイティブドライバー でコンパイルされていません。 =、ただし古いlibmysqlclientを代わりに使用(デフォルトパッケージを使用してUbuntu 13.10でテスト):

_<?php
echo $dbh->getAttribute(PDO::ATTR_CLIENT_VERSION); // prints "5.5.35", i.e MySQL version
// prints "mysqlnd (...)" when using mysqlnd
_

あなたのテストケース( "Edit 4"、with setAttribute(MYSQL_ATTR_USE_BUFFERED_QUERY, true))は、次のようにmysqlndで手動でコンパイルされたPHP 5.5.3で期待どおりに機能します。

_./configure --with-pdo-mysql=mysqlnd # default driver since PHP v5.4
_

...しかし失敗する:

_bash> ./configure --with-pdo-mysql=/usr/bin/mysql_config
_

最初のステートメントが2回実行された場合にのみ失敗するのは非常に奇妙です。これはlibmysqlclientドライバーのバグである必要があります。

_MYSQL_ATTR_USE_BUFFERED_QUERY_がfalseの場合、両方のドライバーは期待どおりに失敗します。 あなたの常識はすでに実証されています 結果セットの行数に関係なく、これが予想される動作である理由。

Mikeは、現在の回避策が、Canonicalが推奨する_php5-mysqlnd_ではなく_php5-mysql_パッケージをインストールしていることを発見しました。

13
RandomSeed

これは必ずしもこの質問に対する答えではありませんが、これは将来誰かを助けるかもしれません。

私はまったく同じエラーに遭遇し、何が悪いのかを発見するのに何時間もかかりました。それはずっと非常にマイナーな構文の問題であることが判明しました。実際にバッファリングを使用していないのに、私と同じようにこのエラーが発生する場合は、これが問題である可能性があります。コードを確認してください。

このエラーに遭遇したとき、私は通常のデータベースクエリを実行していましたが、意図的にバッファリング手法を使用していなかったため、バッファリングとは何の関係もないのではないかと強く疑っていました。私はそれについてのすべてのSO質問を読み、それをより深く調べました。

これは私のSTUPID構文の問題でした:

$SQL = "UPDATE articles SET
            topicID = :topic;    <-------- semicolon - woops!
            heading = :heading,
            subheading = :subheading,
            keywords = :keywords,
            rawContent = :rawContent,
            content = :content,
            ...
            ...

その結果、このバッファリングエラーが発生しました。コードを修正したところ、なくなりました。最も厄介なのは、PDOエラーが別のクエリ、次のクエリを指しているという事実でしたが、そのクエリはコードの他の場所の関数にあり、それは私を通してしばらくコースから外れていました!

3
TheCarver

_PDO::MYSQL_ATTR_USE_BUFFERED_QUERY_がFALSEに設定されているようです。

そして、そのような場合、検索のために保留中の行がもうないことを確認することが義務付けられています。これを行うには、fetch()をもう1回実行します。これは、fetch()がfalseを返すことは、バッファリングされていない結果セットを何らかの形で「解放」しているように見えるためです。このような余分な呼び出しがないと、バッファリングされていない結果セットはロックされたままになり、「コマンドが同期していません」エラーが発生します

2

この問題を引き起こしている可能性のある間違いのリストを完成させるために...私はこれについて髪を失っていたので、私の解決策/発見をあなたと共有したいと思います。

私の場合、PDO :: execを使用してデータベースにいくつかのステートメントを送信しようとしました

例えば.

self::$objDatabase->exec( "SELECT id from testtable; UPDATE testtable SET name = 'example';" );

1つのPDO :: execで1つのSQLステートメントのみが許可されて保存されます。

0
Thomas