【问题标题】:PostgreSQL PHP PDO execute hangs when prepared query omits a NOT NULL field当准备好的查询省略 NOT NULL 字段时,PostgreSQL PHP PDO 执行挂起
【发布时间】:2018-09-12 01:05:38
【问题描述】:

使用 PostgreSQL 9.6 和 PHP 7.2,我正在创建一个 PDO 连接,准备一个 INSERT 查询,绑定参数,然后执行准备好的查询。我将ATTR_ERRMODE 设置为ERRMODE_EXCEPTION,实际上可能会导致执行抛出异常,例如,准备好的查询有4 个参数,但我只绑定了3 个。

但是,如果我准备好的查询不包含具有 NOT NULL 约束的字段,则执行可能会挂起 20 秒,然后浏览器会显示“连接已重置”。 Postgres 日志说:

ERROR:  null value in column "fullname" violates not-null constraint
DETAIL:  Failing row contains (345, fredf, fredf@bedrock.net, yabadabadoo, null, Bedrock, none, user).
STATEMENT:  INSERT INTO users (username, password, location, description, email, role) VALUES ($1, $2, $3, $4, $5, $6)
LOG:  could not receive data from client: An existing connection was forcibly closed by the remote host.

这 4 个日志条目总共重复了 10 次。

我理解这种情况确实是一个编程问题——查询应该符合数据库设计的要求。但我真的很想哄 Postgres PDO 驱动程序告诉我错误是什么,而不是去挖掘日志。


示例代码

/*

Sample code to demonstrate failure of prepared query execute()
to throw an exception when the prepared query omits a field
with a NOT NULL constraint.

CREATE TABLE users (
  id                SERIAL PRIMARY KEY,
  username      varchar(50) NOT NULL,
  email           varchar(100) NOT NULL,
  password      varchar(255) NOT NULL,
  fullname      varchar(100) NOT NULL,
  location      varchar(100) NOT NULL,
  description text NOT NULL,
  role            varchar(50) NOT NULL DEFAULT ''
);

*/
    $host   = "localhost";  
    $user   = "postgres";  
    $pass   = "password";  
    $dbname = "database";  
    /**
     * Initialize the PDO connection. 
     */
        $dsn = 'pgsql:host=' . $host . ';dbname=' . $dbname;
        $options = [
            PDO::ATTR_PERSISTENT => true,  
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION  
            ]; 
        try {  
            $handler = new PDO($dsn, $user, $pass, $options);  
        } catch (PDOException $e) {  
            echo $e->getMessage();
            die();  
        } 

     /**
      * Demonstrate failure of execute() to return an error or throw an exception
      * Error condition omits the fullname field from the INSERT query, which has a 
      * "not null" constraint.
      */
        $demonstrateProblem = 1;

        $badQuery = "INSERT INTO users (username, password, location, description, email, role) " . 
                    "VALUES (:username, :password, :location, :description, :email, :role)";

        $goodQuery = "INSERT INTO users (username, password, fullname, location, description, email, role) " . 
                    "VALUES (:username, :password, :fullname, :location, :description, :email, :role)";

        if ($demonstrateProblem == 1) {
            $query = $badQuery;
        } else  {
            $query = $goodQuery;
        }

        $stmt = $handler->prepare($query);  
        if (!$stmt) {
            echo "Error in prepare, errorInfo():<br>";
            print_r($handler->errorInfo());
        }
    /**
     * Bind the variables
     */
        $username   = "fredf";
        $password   = "yabadabadoo";
        $fullname   = "Fred Flintstone";
        $location   = "Bedrock";
        $description = "none";
        $email      = "fredf@bedrock.net";
        $role       = "user";

        $stmt->bindParam(':username', $username);
        $stmt->bindParam(':password', $password);
        if ($demonstrateProblem == 0) {
            $stmt->bindParam(':fullname', $fullname);
        } else {
            // nothing to do, :fullname is not in the prepared query!
        }
        $stmt->bindParam(':location', $location);
        $stmt->bindParam(':description', $description);
        $stmt->bindParam(':email', $email);
        $stmt->bindParam(':role', $role);
    /**
     * Execute a prepared statement.
     */
        echo "Executing prepared query...<br>";
        try {
            $res = $stmt->execute(); // <-- this statement hangs when the query prepare is bad.
            if ($res) {
                echo "execute returns true<br> ";
            } else {
                echo "execute returns false<br> ";               
            }
        } catch (PDOException $e) {  
            echo "execute error " . $e->getMessage();  // <-- this never appears
        }

PostgreSQL 日志(序列重复 10 次)

 ERROR:  null value in column "fullname" violates not-null constraint
 DETAIL:  Failing row contains (345, fredf, fredf@bedrock.net, yabadabadoo, null, Bedrock, none, user).
 STATEMENT:  INSERT INTO users (username, password, location, description, email, role) VALUES ($1, $2, $3, $4, $5, $6)
 LOG:  could not receive data from client: An existing connection was forcibly closed by the remote host.

【问题讨论】:

  • 您是否使用try ... catch 将您的SQL 调用包装在PHP 中以捕获异常?当然,您可以缩写您的 PHP 代码块,而不需要 100 行。
  • 是的,异常处理程序可以完美地处理其他错误,例如查询包含 4 个参数,但只绑定了 3 个。我会看看我是否可以缩短代码,但要到早上才能做到,谢谢。
  • 你是怎么运行这个的?您是否使用框架来运行它?通过网络?尝试在命令行离线运行。
  • 我从 Apache24 的本地 (Windows 10) 安装运行它。该代码基于我在 github 上找到的小型演示,而不是框架。您认为从 cmd 行运行的想法是一个很好的想法,实际上它确实会以这种方式抛出异常。所以也许这在我的 Apache 配置中有问题——如果您有任何想法,请使用 LMK?
  • 在 Apache 上如何调用 PHP 脚本?导航到其网页?你说的挂是什么意思?如果您正在谈论一个空白的白页,这可能意味着您的错误尝试运行后的代码。检查 apache 错误日志以获取任何消息。尝试在catch 中回显消息后添加exit

标签: php postgresql


【解决方案1】:

好吧,您正在尝试为具有 NOT NULL 约束的字段添加具有 NULL 值的数据。您要么需要在 Postgresql 中为“fullname”列定义默认值,要么将其设为 NULL。

【讨论】:

  • 是的,我的帖子明确指出这是一个编程问题,但问题是:为什么不触发异常?我已经添加了源代码,如果你有什么想法,那就太好了。
  • 也许升级到 PostgreSQL 10 可以解决问题?
  • 我打算试一试。通过查看 apache 日志以确认缺少异常不是由于我的 PHP 代码中的错误,我的直接问题得到了解决。但无论如何,出于多种原因,移动到 10 可能是有意义的。谢谢!
【解决方案2】:

我认为,您需要将准备语句和绑定参数代码放入 try 块中。 希望能解决问题。

【讨论】:

  • 感谢 Sachin,我已经尝试过这个想法,但没有帮助。
  • 我使用了与您相同的代码。现在我得到了异常。我附上了我在另一个答案中使用过的代码。
【解决方案3】:

请找出我使用的代码。

$host   = "";             
$user   = "";     
$pass   = "";         
$dbname = "";        
/**
 * Initialize the PDO connection. 
 */
    $dsn = 'pgsql:host=' . $host . ';dbname=' . $dbname;
    $options = [
        PDO::ATTR_PERSISTENT => true,
        PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
        ];
    try {
        $handler = new PDO($dsn, $user, $pass, $options);
    } catch (PDOException $e) {
        echo $e->getMessage();
        die();
    }


try{
$query = "insert into ********  values(null, 2709651, NOW(), 'test')";             
$stmt = $handler->prepare($query);
$res = $stmt->execute();
if ($res) {
            echo "execute returns true<br> ";
        } else {
            echo "execute returns false<br> ";
        }
}catch(PDOException $e){
echo $e->getMessage();
}

您可以尝试使用此代码。由于我在将 null 插入非 null 列时遇到错误。

SQLSTATE[23502]:非空违规:7 错误:“*******”列中的空值违反非空约束 详细信息:失败行包含 (null, 2709651, 2018-09-13 12:07:03.356125, test)。

这个错误来自异常处理。

【讨论】:

  • 谢谢。经过进一步调查,我遇到的问题似乎与我的 Apache 配置有关,而不是与 PHP 代码有关。 Apache 在处理来自 Postgres 的错误时抛出了自己的异常。
猜你喜欢
  • 1970-01-01
  • 2018-11-20
  • 1970-01-01
  • 1970-01-01
  • 2012-05-16
  • 1970-01-01
  • 1970-01-01
  • 2022-01-10
  • 1970-01-01
相关资源
最近更新 更多