繁体   English   中英

当 PDO::ATTR_EMULATE_PREPARE = false 时 PHP PDO 失败

[英]PHP PDO fails when PDO::ATTR_EMULATE_PREPARE = false

我有一个应用程序,我将一类PDO 连接作为参数传递给 MySQL 8,该类使用该 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;

然后我得到:

致命错误:未捕获的运行时异常:无法执行查询:“ USE pdo_snippet ;”。 PDO 异常消息:SQLSTATE[HY000]:一般错误:2014 无法执行查询,而其他未缓冲的查询处于活动状态。 考虑使用 PDOStatement::fetchAll()。 或者,如果您的代码只针对 mysql 运行,您可以通过设置 PDO::MYSQL_ATTR_USE_BUFFERED_QUERY 属性来启用查询缓冲。

第二次运行脚本:

$dsn = 'mysql:dbname=' .$dbname. ';host=' . $host;

首先 baypass USE 语句抛出的异常给出了另一个异常:

致命错误:未捕获的运行时异常:无法执行查询:“ DROP TRIGGER IF EXISTS 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;这样的语句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] = false; $options[\\PDO::MYSQL_ATTR_USE_BUFFERED_QUERY] = true; 没有帮助。
也许你可以让我清楚这个错误。

经过一些额外的测试,在我看来,问题可能与无缓冲查询有某种关系,因为如果在第二次运行脚本时,我通过添加以下内容运行它:

$options[\PDO::MYSQL_ATTR_USE_BUFFERED_QUERY] = false;

然后脚本失败得更早,在:

致命错误:未捕获的 RuntimeException:无法执行查询:“ CREATE TABLE IF NOT EXISTS 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我相信默认情况下这是真的。 但是仍然存在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 镜像。

你的代码错了

参见手册

DROP TRIGGER IF EXISTS tr_example_bi_fill 

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM