在过去的两天里,当我使用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();
Run Code Online (Sandbox Code Playgroud)
之后,我遍历结果并显示它们没有问题.这基本上意味着我的连接是正确的,否则我不会从数据库中获得任何东西.另一方面,以下使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();
}
Run Code Online (Sandbox Code Playgroud)
Linux和Windows都存在这个问题,我使用的是Vertica版本7.0.2-1以及相应的ODBC驱动程序.Vertica 6.1中也存在这个问题.任何人都可以帮我一把吗?
提前致谢.
编辑:我尝试将PDO :: ATTR_EMULATE_PREPARES设置为true和false,没有任何更改.
编辑:这是一个测试脚本,我没有打扰任何错误处理.此外,鉴于服务器实际崩溃,我怀疑它会改变什么.
编辑:更新了上面的代码,包括一些基本的错误处理.在我之前的评论中向Kermit道歉.无论如何,即使添加了我的代码,我仍然没有得到任何消息,服务器只会静默崩溃,我会得到一个"连接重置"页面.看到这个后,我尝试查询数据库中的不同表格,而不是崩溃,我得到了以下内容:
SQLSTATE [HY000]:常规错误:50310 [Vertica] [支持](50310)无法识别的ICU转换错误.(SQLExecute [50310]在\ text\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 = ?'
Run Code Online (Sandbox Code Playgroud)
显然,似乎PDO正在最终查询中用问号替换占位符.并非所有意外,但由于某种原因,参数的实际值似乎一路上都会丢失.
编辑:根据建议,我尝试:
$stmt = $c->prepare("SELECT * FROM myClients WHERE ClientNum = :cl");
$stmt->execute(array(":cl" => 88));
Run Code Online (Sandbox Code Playgroud)
但问题仍然存在.
好吧,所以在试图弄清楚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;
}
}
}
Run Code Online (Sandbox Code Playgroud)
我惊喜地发现,如果没有我重写数百个模块,这是有效的.我确实需要重新编写一些SQL,因为MySQL和Vertica之间存在差异,但这些只是轻微的修饰.
无论如何,如果有人选择使用这些类,请记住我只在功能方面实现了我需要的东西,它们只使用参数占位符的查询(:someParameter).使用它们并根据自己的判断进行修改.
感谢任何帮助过我的人.