在过去的两天里,当我使用PDO与Vertica连接时,我一直在努力解决一个非常奇怪的错误。您看,以下脚本有效:
$c = new PDO("odbc:Driver=Vertica;Server=x.x.x.x;Port=5433;Database=db;", "MyUser", "MyPassword");
$c->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$stmt = $c->prepare("SELECT * FROM myClients WHERE ClientNum = 88");
$stmt->execute();
之后,我遍历结果并显示它们没问题。这基本上意味着我的连接是正确的,否则我不会从数据库中获取任何东西。另一方面,以下内容使Apache服务器完全重置连接(在Windows中运行时,我收到Apache崩溃的消息):
$c = new PDO("odbc:Driver=Vertica;Server=x.x.x.x;Port=5433;Database=db;", "MyUser", "MyPassword");
$c->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$c->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);
//$c->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
try
{
$stmt = $c->prepare("SELECT * FROM myClients WHERE ClientNum = :cl");
$stmt->bindValue(":cl", 88);
$stmt->execute();
while($res = $stmt->fetch(PDO::FETCH_ASSOC))
{
echo $res['noClient'] . "<br>";
}
}
catch(Exception $e)
{
echo $e->getMessage();
}
Linux和Windows都存在这个问题,我使用Vertica版本7.0.2-1以及相应的ODBC驱动程序。 Vertica 6.1中也存在这个问题。任何人都可以帮我一把吗?
提前致谢。
编辑:我试图将PDO :: ATTR_EMULATE_PREPARES设置为true和false而不做任何更改。
编辑:这是一个测试脚本,我没有打扰任何错误处理。此外,鉴于服务器实际崩溃,我怀疑它会改变任何东西。编辑:更新了上面的代码,包括一些基本的错误处理。在我之前的评论中向Kermit道歉。无论如何,即使添加了我的代码,我仍然没有得到任何消息,服务器只会默默崩溃,我会得到一个&#34;连接重置&#34;页。看到这个后,我尝试在我的数据库中查询不同的表,而不是崩溃,我得到了以下内容:
SQLSTATE [HY000]:常规错误:50310 [Vertica] [支持](50310)无法识别的ICU转换错误。 (SQLExecute [50310]在ext \ pdo_odbc \ odbc_stmt.c:254)
编辑:转到我的ODBC DSN,单击配置,进入服务器设置选项卡,发现语言环境设置为:en_US @ collation = binary(这是Vertica的默认设置,我相信)。我应该去别的地方查一下吗?编辑:我很想知道bindValue()对我的查询做了什么,因此打开了vertica.log文件。这就是我所看到的:
2014-10-02 11:38:42.100 Init Session:0x5ef3030 [Session] <INFO> [Query] TX:0(vertica-1756:0xbc42) set session autocommit to on
2014-10-02 11:38:42.104 Init Session:0x5ef3030 [Session] <INFO> [PQuery] TX:0(vertica-1756:0xbc42) SELECT * FROM myClients WHERE ClientNum = ?
2014-10-02 11:38:42.105 Init Session:0x5ef3030-a00000000aac68 [Txn] <INFO> Begin Txn: a00000000aac68 'SELECT * FROM myClients WHERE ClientNum = ?'
2014-10-02 11:38:42.915 Init Session:0x5ef3030-a00000000aac68 <LOG> @v_flexgroup_node0001: 08006/2895: Could not receive data from client: No such file or directory
2014-10-02 11:38:42.915 Init Session:0x5ef3030-a00000000aac68 <LOG> @v_flexgroup_node0001: 08006/5167: Unexpected EOF on client connection
2014-10-02 11:38:42.915 Init Session:0x5ef3030-a00000000aac68 <LOG> @v_flexgroup_node0001: 00000/4719: Session vertica-1756:0xbc42 ended; closing connection (connCnt 2)
2014-10-02 11:38:42.916 Init Session:0x5ef3030-a00000000aac68 [Txn] <INFO> Rollback Txn: a00000000aac68 'SELECT * FROM myClients WHERE ClientNum = ?'
显然,似乎PDO正在最终查询中用问号替换占位符。并非所有意外,但由于某种原因,参数的实际值似乎一路上都会丢失。
编辑:根据建议,我尝试了:
$stmt = $c->prepare("SELECT * FROM myClients WHERE ClientNum = :cl");
$stmt->execute(array(":cl" => 88));
但问题仍然存在。
答案 0 :(得分:5)
好的,所以在试图找出PDO出了什么问题之后疯了一半,我发现使用PHP odbc模块直接工作了。
由于我的所有模块实际上都是使用PDO编写的,并且重写它们不是一个选项,我最后编写了以下包装类:
class PDOVertica
{
protected $conn;
public function __construct($dsn, $user, $password)
{
$this->conn = odbc_connect($dsn, $user, $password);
}
public function prepare($qry)
{
return new PDOVerticaStatement($this->conn, $qry);
}
public function lastInsertId()
{
$stmt = odbc_prepare($this->conn, "SELECT LAST_INSERT_ID()");
odbc_execute($stmt);
$res = odbc_fetch_array($stmt);
return $res['LAST_INSERT_ID'];
}
}
class PDOVerticaStatement
{
protected $qry;
protected $param;
protected $stmt;
public function __construct($conn, $qry)
{
$this->qry = preg_replace('/(?<=\s|^):[^\s:]++/um', '?', $qry);
$this->param = null;
$this->extractParam($qry);
$this->stmt = odbc_prepare($conn, $this->qry);
}
public function bindValue($param, $val)
{
$this->param[$param] = $val;
}
public function execute()
{
if($this->param == null)
odbc_execute($this->stmt);
else
odbc_execute($this->stmt, $this->param);
$this->clearParam();
}
public function fetch($option)
{
return odbc_fetch_array($this->stmt);
}
protected function extractParam($qry)
{
$qryArray = explode(" ", $qry);
$ind = 0;
while(isset($qryArray[$ind]))
{
if(preg_match("/^:/", $qryArray[$ind]))
$this->param[$qryArray[$ind]] = null;
++$ind;
}
}
protected function clearParam()
{
$ind = 0;
while(isset($this->param[$ind]))
{
$this->param[$ind] = null;
++$ind;
}
}
}
我惊喜地发现,如果没有我重写数百个模块,这是有效的。我确实需要重新编写一些SQL,因为MySQL和Vertica之间存在差异,但这些只是轻微的修饰。
无论如何,如果有人选择使用这些类,请记住我只在功能方面实现了我需要的东西,它们只使用参数占位符的查询(:someParameter)。使用它们并根据自己的判断进行修改。
感谢所有帮助过我的人。