2014-10-01 104 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