Vertica и PDO подготовили заявления

Последние два дня я боролся с очень странной ошибкой, пока я подключался к 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 я получаю сообщение о сбое 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 без каких-либо изменений.

РЕДАКТИРОВАТЬ: Это тестовый сценарий, я не беспокоился об обработке ошибок. Кроме того, учитывая, что сервер действительно дает сбой, я сомневаюсь, что это что-то изменит.

РЕДАКТИРОВАТЬ: обновленный код выше, чтобы включить некоторые основные обработки ошибок Извиняюсь перед Кермитом за звучание снисходительного в моем предыдущем комментарии. В любом случае, даже с этим дополнением к моему коду я все еще не получил никакого сообщения, сервер просто молча зависал, и я получал страницу «Сброс соединения». Увидев это, я попытался запросить разные таблицы в своей базе данных, и вместо одной из них я получил следующее:

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

Но проблема остается той же.

2

Решение

Итак, после того, как я на полпути сошел с ума, пытаясь выяснить, что не так с 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). Используйте их и модифицируйте по своему усмотрению.

Спасибо всем, кто мне помог.

5

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]