Что является причиной ошибки 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();

Однако по какой-то причине я получаю следующую ошибку при выполнении второго подготовленного оператора:

Неустранимая ошибка: необработанное исключение "PDOException" с сообщением "SQLSTATE[HY000]: общая ошибка: 2014 г. Невозможно выполнить запросы, когда другие небуферизованные запросы активны. Рассмотрите возможность использования PDOStatement::fetchAll(). В качестве альтернативы, если ваш код работает только с mysql, вы можете включить буферизацию запросов, установив атрибут PDO::MYSQL_ATTR_USE_BUFFERED_QUERY.'

Я знаю, что означает эта ошибка и как ее исправить (либо делаю unset($stmt); или же $stmt->closeCursor();), поэтому я не ищу решения, как заставить его работать. Из того, что я понимаю, это обычно вызывается fetch вместо fetchAll и не получить все результаты. Однако в этом случае есть только один результат, и он выбирается. Кроме того, если я выполню только первый подготовленный оператор один раз, ошибка не произойдет. Это происходит только тогда, когда первый оператор выполняется дважды. Это также происходит только тогда, когда PDO::ATTR_EMULATE_PREPARES является false,

Итак, мой вопрос, что является причиной вышеуказанной ошибки в этом случае? Похоже, он ничем не отличается от любого другого запроса, который я когда-либо выполнял.

Я проверил это на двух серверах Ubuntu 13.10, Debian и CentOS, и все они выдают одинаковую ошибку, используя пакеты по умолчанию.

Редактировать:

Чтобы ответить на комментарий Райана Винсента, я - полный нуб, но я полагаю, что то, что я имею ниже, примерно эквивалентно приведенному выше примеру. Пожалуйста, поправьте меня, если я ошибаюсь. Однако он не выдает ошибок, поэтому может показаться, что это ошибка только для 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();

4 ответа

Решение

Как ни странно, пакеты PHP, предоставляемые Ubuntu, не скомпилированы с собственным драйвером 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

Ваш самый контрольный пример ("Редактировать 4", с setAttribute(MYSQL_ATTR_USE_BUFFERED_QUERY, true)) работает, как и ожидалось, с PHP 5.5.3, скомпилированным вручную с помощью mysqlnd:

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

... но терпит неудачу с:

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

Это довольно странно, что это терпит неудачу, только если первый оператор выполняется дважды; это должно быть ошибка в драйвере libmysqlclient.

Оба драйвера терпят неудачу, как и ожидалось, когда MYSQL_ATTR_USE_BUFFERED_QUERY является false, Ваш здравый смысл уже продемонстрировал, почему это ожидаемое поведение, независимо от количества строк в наборе результатов.

Майк узнал, что текущим обходным путем является установка php5-mysqlnd пакет вместо Canonical-рекомендуется php5-mysql,

Это не обязательно ответ на этот вопрос, но это может помочь кому-то в будущем.

Я столкнулся с точно такой же ошибкой, и потребовались часы, чтобы выяснить, в чем дело. Оказалось, что это была исключительно незначительная проблема синтаксиса. Если вы на самом деле не используете буферизацию, но у вас все еще есть эта ошибка, как я, это может быть вашей проблемой - так что проверьте ваш код.

Я выполнял свои обычные запросы к базе данных, когда натолкнулся на эту ошибку - не нарочно используя какие-либо методы буферизации - поэтому я сильно сомневался, что это как-то связано с буферизацией. Я прочитал каждый ТАК вопрос об этом и посмотрел глубже к нему.

Это была моя проблема с синтаксисом STUPID:

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

Это привело к тому, что я получил эту ошибку буферизации. Я исправил код, и он ушел. Что больше всего раздражало, так это то, что ошибка PDO указывала на другой запрос, на следующий запрос, но этот запрос находился в функции в другом месте кода, и это через некоторое время было хорошо для меня!

Кажется, у вас есть PDO::MYSQL_ATTR_USE_BUFFERED_QUERY установить в ЛОЖЬ.

И в таком случае обязательно убедиться, что больше нет строк, ожидающих получения. Для этого нужно бежать fetch() один дополнительный раз, так как кажется, что fetch() возвращение false как-то "освобождает" небуферизованный набор результатов. Без такого дополнительного вызова небуферизованный набор результатов остается заблокированным и вызывает ошибку "Команды не синхронизированы"

Просто для того, чтобы завершить список возможных ошибок, вызывающих эту проблему... потому что я терял голову на этом, я хочу поделиться своим решением / находкой с вами.

В моем случае я попытался отправить несколько операторов в базу данных с помощью PDO::exec

например

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

Разрешается и сохраняется только 1 оператор SQL в 1 PDO::exec.

Другие вопросы по тегам