2014-10-01 160 views
2

在过去的两天中,我一直在使用PDO与Vertica连接时遇到了一个非常奇怪的错误。你看,以下脚本的工作原理如下:Vertica和PDO准备的语句

$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中运行的时候,我得到一个消息,阿帕奇坠毁):

$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 V7.0.2-1以及相应的ODBC驱动程序。 Vertica 6.1中也存在这个问题。任何人都可以帮我一把吗?

在此先感谢。

编辑:我试图设置PDO :: ATTR_EMULATE_PREPARES真假没有任何改变。

编辑:这是一个测试脚本,我没有打扰任何错误处理。另外,鉴于服务器实际崩溃,我怀疑它会改变任何事情。

编辑:更新了上面的代码以包含一些基本的错误处理。在我早些时候的评论中,Kermit对于听起来居高临下的道歉抱歉。无论如何,即使对我的代码添加了这些内容,我仍然没有收到任何消息,服务器只会默默地崩溃,我会得到一个“连接重置”页面。看到这个,我试着查询我的数据库中的不同表格,并在一个,而不是崩溃,我得到以下:

SQLSTATE [HY000]:一般错误:50310 [Vertica] [Support](50310)Unrecognized ICU转换错误。 (SQLExecute [50310]在内线\ PDO_ODBC \ odbc_stmt.c:254)

编辑:去我的ODBC DSN,点击配置,走到服务器设置选项卡上,发现区域被设置为:EN_US @整理= 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

尝试反转为'PDO :: ATTR_EMULATE_PREPARES'当前设置。如果它现在是'true',则将其设为'false',反之。我怀疑你现在没有使用模拟的准备工作,但是这样做可能会有所帮助,因为在发送到RDBMS之前,PDO/PHP将处理所有的参数绑定和替换。 (我有Vertica的没有经验) – 2014-10-01 20:27:18

+0

这里记录http://php.net/manual/en/pdo.setattribute.php – 2014-10-01 20:28:18

+0

哪里是你的错误处理? – Kermit 2014-10-01 20:52:43

回答

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; 
     } 
    } 
} 

我惊喜地发现,这工作,而不必给我重写数百个模块。由于MySQL和Vertica之间存在差异,我确实需要重新修改一些SQL,但这些仅仅是小小的修改。

无论如何,任何人都应该选择使用这些类,记住我只实现我需要在功能方面是什么,他们只使用占位符参数的查询工作(:someParameter)。使用它们并自行修改它们。

感谢的人谁帮助了我。

+0

非常酷。有投票权 – Kermit 2014-10-07 15:01:07