#php #mysql #pdo #prepared-statement #setattribute
#php #mysql #pdo #подготовленное заявление #setattribute
Вопрос:
У меня есть приложение, в котором я передаю класс подключения PDO к MySQL 8 в качестве параметра классу, который взаимодействует с базой данных с помощью этого экземпляра подключения PDO.
Для создания PDO-соединения с базой данных я должен использовать параметр PDO::ATTR_EMULATE_PREPARES, для которого для PDO-соединения установлено значение false. Класс, который использует PDO-соединение, не имеет проблем с выполнением операторов SELECT или INSERT, но в случае таких операторов, как:
ИСПОЛЬЗУЙТЕ database-name;
СОЗДАЙТЕ ТРИГГЕР
СБРОСА ТРИГГЕРА
Я получаю сообщение об ошибке:
SQLSTATE[HY000]: Общая ошибка: 2014 не может выполнять запросы, пока активны другие небуферизованные запросы. Рассмотрите возможность использования PDOStatement::fetchAll() . В качестве альтернативы, если ваш код будет выполняться только с mysql, вы можете включить буферизацию запросов, установив атрибут PDO ::MYSQL_ATTR_USE_BUFFERED_QUERY .
Но такие утверждения, как:
СОЗДАТЬ БАЗУ
ДАННЫХ СОЗДАТЬ ТАБЛИЦУ
работает нормально.
Вот пример кода для репликации проблемы:
<?php
header("Content-Type: text/plain");
$user = '';
$pass = '';
$host = '';
$dbname = 'pdo_snippet';
/**
* Use at first run, the script fails at USE database-name statement
* but creates database schema $dbName
*/
$dsn = 'mysql:host=' . $host;
/**
* Use at second run, baypasses fail at USE database-name statement
* but fails at DROP TRIGGER IF EXISTS;
*/
// $dsn = 'mysql:dbname=' .$dbname. ';host=' . $host;
$options = [];
$options[PDO::ATTR_ERRMODE] = PDO::ERRMODE_EXCEPTION;
$options[PDO::ATTR_EMULATE_PREPARES] = false; # with true no problem
//---------- CONNECT TO DATABASE SERVER ---------
echo "Connect to database";
try {
$pdo = new PDO($dsn, $user, $pass, $options);
} catch (Exception $e) {
$msg = 'PDO could not establish connection to dsn: ' . $dsn;
$msg .= '. PDO exception Msg: ' . $e->getMessage();
throw new RuntimeException($msg);
}
echo ", DONE.n";
//----------------------------------------------
//--------- CREATE SCHEMA IF NOT EXISTS --------
$sqlQuery = 'CREATE DATABASE IF NOT EXISTS `' . $dbname . '`;';
echo "Create database $dbname IF NOT EXISTS";
try {
$stmt = $pdo->query($sqlQuery);
} catch (Exception $e) {
$msg = 'Could not execute query: "' . $sqlQuery . '"';
$msg .= '. PDO exception Msg: ' . $e->getMessage();
throw new RuntimeException($msg);
}
echo ", DONE.n";
//------------------------------------------------
//----------- SELECT DEFAULT DATABASE ------------
echo "SELECT FROM default database";
$sqlQuery = "SELECT DATABASE();";
try {
$stmt = $pdo->query($sqlQuery);
} catch (Exception $e) {
$msg = 'Could not execute query: "' . $sqlQuery . '"';
$msg .= '. PDO exception Msg: ' . $e->getMessage();
throw new RuntimeException($msg);
}
echo ", DONE.n";
//------------------------------------------------
$defaultDbName = $stmt->fetchColumn();
//------------------------------------------------
//----------- USE datatabase as default ----------
if (empty($defaultDbName)) {
echo "USE $dbname";
$sqlQuery = 'USE `' . $dbname . '`;';
try {
$stmt = $pdo->query($sqlQuery);
} catch (Exception $e) {
$msg = 'Could not execute query: "' . $sqlQuery . '"';
$msg .= '. PDO exception Msg: ' . $e->getMessage();
throw new RuntimeException($msg);
}
echo ", DONE.n";
}
//------------------------------------------------
//------------ CREATE TABLE ----------------------
echo "CREATE TABLE example IF NOT EXISTS";
$sqlQuery = 'CREATE TABLE IF NOT EXISTS `example` (`id` INT AUTO_INCREMENT, `name` VARCHAR(255), PRIMARY KEY(`id`)) ENGINE=InnoDb;';
try {
$stmt = $pdo->query($sqlQuery);
} catch (Exception $e) {
$msg = 'Could not execute query: "' . $sqlQuery . '"';
$msg .= '. PDO exception Msg: ' . $e->getMessage();
throw new RuntimeException($msg);
}
echo ", DONE.n";
//------------------------------------------------
//------------ DROP TRIGGER IF EXISTS ------------
echo "DROP TRIGGER IF EXISTS";
$sqlQuery = 'DROP TRIGGER IF EXISTS `tr_example_bi_fill`;';
try {
$stmt = $pdo->query($sqlQuery);
} catch (Exception $e) {
$msg = 'Could not execute query: "' . $sqlQuery . '"';
$msg .= '. PDO exception Msg: ' . $e->getMessage();
throw new RuntimeException($msg);
}
echo ", DONE.n";
//------------------------------------------------
//--------------- CREATE TRIGGER ---------------------
echo "CREATE TRIGGER";
$sqlQuery = "
CREATE TRIGGER
`tr_example_bi_fill`
BEFORE INSERT ON
`example`
FOR EACH ROW BEGIN
SET new.`name` = CONCAT(new.`name`, '_TRIGGER');
END
;
";
echo ", DONE.n";
try {
$stmt = $pdo->query($sqlQuery);
} catch (Exception $e) {
$msg = 'Could not execute query: "' . $sqlQuery . '"';
$msg .= '. PDO exception Msg: ' . $e->getMessage();
throw new RuntimeException($msg);
}
//------------------------------------------------
//--------------- INSERT INTO --------------------
$value = rand(0,9999);
echo "INSERT INTO TABLE example VALUE '$value'";
$sqlQuery = "INSERT INTO `example` (`name`) VALUES ('$value')";
try {
$stmt = $pdo->query($sqlQuery);
} catch (Exception $e) {
$msg = 'Could not execute query: "' . $sqlQuery . '"';
$msg .= '. PDO exception Msg: ' . $e->getMessage();
throw new RuntimeException($msg);
}
echo ", DONE.n";
//------------------------------------------------
// --------------- SELECT FROM -------------------
echo "SELECT FROM example";
$sqlQuery = "SELECT `name` FROM `example` ORDER BY `id` DESC LIMIT 1;";
try {
$stmt = $pdo->query($sqlQuery);
} catch (Exception $e) {
$msg = 'Could not execute query: "' . $sqlQuery . '"';
$msg .= '. PDO exception Msg: ' . $e->getMessage();
throw new RuntimeException($msg);
}
echo ", DONE.n";
//------------------------------------------------
$result = $stmt->fetchColumn();
echo "Result:n";
print_r($result);
echo "n=================n";
Если я запускаю его в первый раз с:
$dsn = 'mysql:host=' . $host;
затем я получаю:
Неустранимая ошибка: неперехваченное исключение RuntimeException: не удалось выполнить запрос: «USE
pdo_snippet
;». Сообщение об исключении PDO: SQLSTATE[HY000]: Общая ошибка: 2014 не может выполнять запросы, пока активны другие небуферизованные запросы. Рассмотрите возможность использования PDOStatement::fetchAll() . В качестве альтернативы, если ваш код будет выполняться только с mysql, вы можете включить буферизацию запросов, установив атрибут PDO::MYSQL_ATTR_USE_BUFFERED_QUERY .
Running the script at the second time with:
$dsn = 'mysql:dbname=' .$dbname. ';host=' . $host;
to baypass first the exception thrown by USE statement gives another exception:
Fatal error: Uncaught RuntimeException: Could not execute query:
«DROP TRIGGER IF EXISTStr_example_bi_fill
;». PDO exception Msg:
SQLSTATE[HY000]: General error: 2014 Cannot execute queries while
other unbuffered queries are active. Consider using
PDOStatement::fetchAll(). Alternatively, if your code is only ever
going to run against mysql, you may enable query buffering by setting
the PDO::MYSQL_ATTR_USE_BUFFERED_QUERY attribute.
Я хотел бы спросить, как использовать подписанный экземпляр подключения PDO к базе данных с параметром PDO::ATTR_EMULATE_PREPARES, установленным в false, и иметь возможность выполнять инструкции типа USE database-name;
или DROP TRIGGER ...;
CREATE TRIGGER ...;
?
Меня смущает сообщение об ошибке, которое фокусируется на unbuffered queries
.
IMO нет проблем с буферизованными / небуферизованными запросами, и использование $options[PDO::MYSQL_ATTR_USE_BUFFERED_QUERY] = false;
or $options[PDO::MYSQL_ATTR_USE_BUFFERED_QUERY] = true;
не помогает.
Возможно, вы могли бы прояснить эту ошибку для меня.
После некоторого дополнительного тестирования мне кажется, что проблема может быть как-то связана с небуферизованными запросами, потому что, если при втором запуске скрипта я запускаю его с добавлением:
$options[PDO::MYSQL_ATTR_USE_BUFFERED_QUERY] = false;
тогда скрипт завершается с ошибкой намного раньше, в:
Неустранимая ошибка: Неперехваченное исключение RuntimeException: не удалось выполнить запрос: «СОЗДАТЬ ТАБЛИЦУ, ЕСЛИ ОНА НЕ СУЩЕСТВУЕТ
example
(id
INT AUTO_INCREMENT,name
VARCHAR(255), PRIMARY KEY(id
)) ENGINE=InnoDB;». Сообщение об исключении PDO: SQLSTATE[HY000]: Общая ошибка: 2014 не может выполнять запросы, пока активны другие небуферизованные запросы. Рассмотрите возможность использования PDOStatement::fetchAll() . В качестве альтернативы, если ваш код будет выполняться только с mysql, вы можете включить буферизацию запросов, установив атрибут PDO ::MYSQL_ATTR_USE_BUFFERED_QUERY .
не доходя до проблемной линии с. DROP TRIGGER IF EXISTS
Таким образом, похоже, что, по крайней мере, для таких операторов, как CREATE TABLE, решением может быть использование $options[PDO::MYSQL_ATTR_USE_BUFFERED_QUERY] = true
, которое, как я полагаю, является true по умолчанию. Но все же проблема с USE ...
и CREATE TRIGGER ...
, DROP TRIGGER ...
остается.
Я использую: PHP 7.3.8 (cli) (построен: 2 августа 2019 05:16:32) (NTS ) с расширениями, связанными с MySQL: PDO 7.3.8, mysqli 7.3.8, mysqlnd 5.0.12-dev, pdo_mysq 7.3.8
MySQL 8.0.19
оба основаны на официальных изображениях Docker.
Комментарии:
1. В этом вопросе недостаточно информации. Не могли бы вы, пожалуйста, удалить каждый оператор try-catch из этого кода, запустить его снова и предоставить точный результат, который вы получаете?
2. Кроме того, версия PHP и упоминается ли «mysqlnd» в выводе phpinfo()
3. FWIW У меня было много проблем при попытке использовать emulate_prepares, для которого установлено значение false, и всегда позволять PDO обрабатывать вещи, устанавливая для него значение true. Тем не менее, какие версии PHP и MySQL вы используете и какая версия mysqlnd используется?
4. @Yourcommon Sense Я отредактировал вопрос, чтобы было более очевидно, какое исключение генерируется и когда, и добавил в конце версию PHP с ее расширениями.
5. @Dave ответил редактированием вопроса в конце.
Ответ №1:
Комментарии:
1. К сожалению, я, вероятно, ввел вас в заблуждение с помощью неправильного оператора echo (я изменил его), фактический запрос: $SqlQuery = ‘DROP TRIGGER IF EXISTS
tr_example_bi_fill
;’; корректен и хорошо работает, когда PDO::ATTR_EMULATE_PREPARES имеет значение true .USE ...
оператор также работает нормально, но оба они терпят неудачу, когда PDO::ATTR_EMULATE_PREPARES имеет значение false .2. есть ли веская причина, по которой оно должно быть false? Кажется, это ошибка, которая существует уже давно и не исправлена, вам следует попробовать новые предварительные версии php, если они исправили это.
3. Установка PDO ::ATTR_EMULATE_PREPARES в значение FALSE позволяет использовать multiexec при эмуляции вставок, это быстрее при одновременной вставке огромного количества.
4. просто закройте первое соединение с помощью и откройте второе для false . Если вам нужно переключаться между обоими, откройте оба