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