为什么 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 对 true
和 false
的默认表示。
或者,您可以根据 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);
我试图将数据从一个数据库复制到另一个数据库。两者具有 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 对 true
和 false
的默认表示。
或者,您可以根据 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);