为什么 PDO for Postgres return integer for an boolean field

Why does PDO for Postgres return integer for an boolean field

我试图将数据从一个数据库复制到另一个数据库。两者具有 100% 相同的结构。

当我通过 PDO 获取一行时,我得到整数 0/1 而不是布尔字段的原生 false/true。这是一个问题,当我尝试使用 PDO 将数据插入到第二个数据库时导致此错误消息:

column "disabled" is of type boolean but expression is of type integer

有没有简单的出路?我不知道的选项?因为我在这里谈论的是数百个不同的布尔字段。单独铸造它们是不可能的。如果没有简单的方法,我将不得不阅读列类型并据此采取行动。但我想知道为什么 PDO 会这样。

我正在使用 Postgres 9.0.1 和 PHP 5.5.6

附加信息:

这里是一些相关的代码片段。抱歉,代码太复杂,无法全部显示。 它适用于除布尔值以外的所有字段:

        $db = new PDO(
                "pgsql:host=$host;port=$port;dbname=$name",
                $user,
                $pass,
                array(
                    PDO::ATTR_PERSISTENT => $persistent,
                    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
                )
        );
        $sql = 'SELECT * FROM ...' 

        $sth = $db->query($sql);

        $row = $sth->fetch(PDO::FETCH_ASSOC);

        $sql = "INSERT INTO $tableName (".implode(', ',$cols).") VALUES (:".implode(', :', $cols).")";

        $sthInsert = $dbInsert->prepare($sql);

        foreach ($row as $k=>&$v) {
            $sthInsert->bindParam($k, $v);
        }

        $sthInsert->execute();

不回答你的问题,而是提出一种更直接的方法,并通过在 SQL:

中全部完成来避免 PHP 过境
insert into t (col1, col2)
select a, b
from dblink('dbname=the_other_db', 'select a, b from t') as t_other(a integer, b boolean)

http://www.postgresql.org/docs/current/static/dblink.html

如果在 PHP 中有一些您不知道如何在 SQL 中进行的处理,请在另一个问题中询问。

好的,看来这是最快的方法了。它有效,但我认为这是一个错误,直到有人能解释我为什么需要这样做:

$sql = "SELECT column_name, data_type FROM information_schema.columns WHERE table_name = '$tableName'";

$typeRows = DBH::fetchAll($sql);

foreach ($typeRows as &$tr) {
     $columnTypes[$tr['column_name']] = $tr['data_type'];
} unset($tr);

...

switch ($columnTypes[$k]) {
    case 'boolean' :
         $v = $v==1;
         $sthInsert->bindParam($k, $v, PDO::PARAM_BOOL);
         break;
    default :
         $sthInsert->bindParam($k, $v);
}

你有一个数组 $cols 我不知道那是什么,但我已经在下面提出了一个解决方案,如果你不想按照建议从数据库中获取信息,如何定义它另一个答案。此外,我还认为您需要将 : 添加到 bindParam 键值中。

$cols = array('col1' => 's', 'col2' => 'b');

    $sql = "INSERT INTO $tableName (".implode(', ',array_keys($cols)).") VALUES (:".implode(', :', array_keys($cols)).")";

    $sthInsert = $dbInsert->prepare($sql);

    foreach ($row as $k=>&$v) {
        switch($cols[$k]){
            // null
            case 'n':
                $sthInsert->bindParam(':' . $k, $v, PDO::PARAM_NULL);
                break;
            // bool
            case 'b':
                $sthInsert->bindParam(':' . $k, $v, PDO::PARAM_BOOL);
                break;
            // int
            case 'i':
                $sthInsert->bindParam(':' . $k, $v, PDO::PARAM_INT);
                break;
            // str
            case 's':
                $sthInsert->bindParam(':' . $k, $v, PDO::PARAM_STR);
                break;
            // lob
            case 'l':
                $sthInsert->bindParam(':' . $k, $v, PDO::PARAM_LOB);
                break;
            default:
                $sthInsert->bindParam(':' . $k, $v);
                break;
        }
    }

不是没有正确处理布尔值的列检索,而是值的后续绑定。

您可以通过简单的 select:

来检查这一点
$result = $db->query('Select true as test_truth, false as test_falsehood;');
foreach ( $x as $row ) { var_dump($row); }
/*
array(2) {
  ["test_truth"]=>
  bool(true)
  ["test_falsehood"]=>
  bool(false)
}
*/

问题在于,在后续准备查询中绑定变量时,PDO 将其强制转换为字符串,因为 PDOStatement::bindParam$data_type 的默认值为 PDO::PARAM_STR ( as documented here).

(string)true'1'(string)false''。然后将这些传递给 Postgres 连接,后者无法将它们转换回布尔值,从而给出错误。

因此,解决方法不需要任何关于预期数据类型的元数据,只需要传入值的实际类型。一种方法是:

 if ( is_bool($value) ) {
     $value = ( $value ? 't' : 'f' );
 }
 $statement->bindParam($k, $value);

这有效地创建了一个字符串的替代转换,使用 't''f',它们分别是 Postgres 对 truefalse 的默认表示。

或者,您可以根据 PHP 变量类型设置参数类型:

switch ( gettype($param) ) {
     case 'boolean':
          $param_type = PDO::PARAM_BOOL;
     break;
     // other cases here as necessary
     // see http://php.net/gettype and http://php.net/manual/en/pdo.constants.php
     default:
          $param_type = PDO_PARAM_STR;
     break;
}
$statement->bindParam($k, $param, $param_type);