Vertica和PDO编写了声明

时间:2014-10-01 20:24:28

标签: php pdo vertica

在过去的两天里,当我使用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 @ collat​​ion = 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));

但问题仍然存在。

1 个答案:

答案 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)。使用它们并根据自己的判断进行修改。

感谢所有帮助过我的人。

相关问题