【发布时间】:2021-03-05 11:18:59
【问题描述】:
我有一个应用程序,其中我将一个 PDO connection 类作为参数传递给一个使用该 PDO 连接实例与数据库交互的类的参数。
为了创建到数据库的 PDO 连接,我必须使用选项 PDO::ATTR_EMULATE_PREPARES 设置为 false 进行 PDO 连接。使用 PDO 连接的类在执行 SELECT 或 INSERT 语句时没有问题,但在以下语句的情况下:
使用数据库名称;
创建触发器
删除触发器
我收到一个错误:
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 属性进行缓冲。
第二次运行脚本:
$dsn = 'mysql:dbname=' .$dbname. ';host=' . $host;
首先要通过 USE 语句抛出的异常给出另一个异常:
致命错误:未捕获的 RuntimeException:无法执行查询: “如果存在则删除触发器
tr_example_bi_fill;”。 PDO 异常消息: SQLSTATE [HY000]:一般错误:2014 无法执行查询,而 其他无缓冲查询处于活动状态。考虑使用 PDOStatement::fetchAll()。或者,如果您的代码只是 要针对 mysql 运行,您可以通过设置启用查询缓冲 PDO::MYSQL_ATTR_USE_BUFFERED_QUERY 属性。
我想问一下如何使用 PDO 连接到数据库的单一实例,其中选项 PDO::ATTR_EMULATE_PREPARES 设置为 false 并能够执行像 USE database-name; 或 DROP TRIGGER ...; 这样的语句CREATE TRIGGER ...;?
关注unbuffered queries 的错误消息让我感到困惑。
IMO 缓冲/非缓冲查询没有问题,使用 $options[\PDO::MYSQL_ATTR_USE_BUFFERED_QUERY] = false; 或 $options[\PDO::MYSQL_ATTR_USE_BUFFERED_QUERY] = true; 无济于事。
也许你可以把这个错误告诉我。
经过一些额外的测试,在我看来问题可能与无缓冲的查询有关,因为如果在第二次运行脚本时我运行它并添加:
$options[\PDO::MYSQL_ATTR_USE_BUFFERED_QUERY] = false;
然后脚本更早地失败了,在:
致命错误:未捕获的 RuntimeException:无法执行查询: "如果不存在则创建表
example(idINT AUTO_INCREMENT,nameVARCHAR(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,我相信默认情况下这是正确的。但是USE ... 和CREATE TRIGGER ...、DROP TRIGGER ... 的问题仍然存在。
我正在使用: PHP 7.3.8(cli)(构建:2019 年 8 月 2 日 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 镜像。
【问题讨论】:
-
问题中没有足够的信息。您能否从这段代码中删除每一个 try-catch 语句,再次运行它并提供您得到的确切输出?
-
另外,PHP 版本以及 phpinfo() 输出中是否提到“mysqlnd”
-
FWIW 在尝试将 emulate_prepares 设置为 false 并始终让 PDO 将其设置为 true 来处理事情时,我遇到了很多问题。也就是说,您使用的是什么版本的 PHP 和 MySQL,使用的是什么版本的 mysqlnd?
-
@YourCommonSense 我编辑了这个问题,所以更明显的是抛出了什么异常以及何时并在最后添加了 PHP 版本及其扩展。
-
@Dave 最后回答了问题的编辑。
标签: php mysql pdo prepared-statement setattribute