本文件基於 CentOS + Laravel 6.x 版本,其他環境未經過驗證
一、通過 ODBC 連線 Vertica
**Vertica所給的資料庫驅動有兩種:JDBC 和 ODBC。JDBC 是 java語言使用,ODBC 可提供給 PHP 使用。本文件針對在Unix、Linux環境下,在php語言CI框架中使用ODBC連線Vertica的使用。
安裝 unixODBC
UnixODBC 是一個連線資料庫的元件,可以使在 Unix、Linux 系統下使用 ODBC
yum install unixODBC
yum install unixODBC-devel
檢視版本和配置檔案
odbcinst -j
結果如下:
unixODBC 2.2.14
DRIVERS............: /etc/odbcinst.ini
SYSTEM DATA SOURCES: /etc/odbc.ini
FILE DATA SOURCES..: /etc/ODBCDataSources
USER DATA SOURCES..: /root/.odbc.ini
SQLULEN Size.......: 8
SQLLEN Size........: 8
SQLSETPOSIROW Size.: 8
安裝關於 Vertica ODBC 的驅動
Wget https://my.vertica.com/client_drivers/9.0.x/9.0.1-4/vertica-client-9.0.1-4.x86_64.rpm
rpm -ivh vertica-client-9.0.1-4.x86_64.rpm
ldd /opt/vertica/lib64/libverticaodbc.so
結果如下:
export PATH=$PATH:/opt/vertica/bin
配置 ODBC
vi /etc/odbc.ini
[ODBC Data Sources]
VerticaDSNunixodbc = vertica1
[VerticaDSNunixodbc]
Description = Vertica Database using ODBC Driver
Driver = /opt/vertica/lib64/libverticaodbc.so
Servername = vertica
Database = db1
UserName = username #UID
Password = password #PWD
Port = 5433
ReadOnly = true
# ResultBufferSize =131072 (128KB)
驗證是否安裝配置成功
isql -v VerticaDSNunixodbc username password
結果如下:
配置高可用 vertica
vi /etc/odbc.ini
新增配置項:BackupServerNode
修改後的配置如下:
如果需要在本地進行測試,可修改/ets/hosts,使高可用域名暫時失效,測試不同node下的切換是否有效。
改後的hosts檔案如下:
192.168.0.1 node1.vertica.api.local.datapaas.cn
192.168.0.2 node2.vertica.api.local.datapaas.cn
192.168.0.3 node3.vertica.api.local.datapaas.cn
192.168.0.4 node4.vertica.api.local.datapaas.cn
192.168.0.5 node5.vertica.api.local.datapaas.cn
比如測試node6是否可使用,將最後一行更改為高可用IP匹配node6域名即可
xx.xxx.xxx.xx node6.addnewer-vertica.api.local.datapaas.cn
安裝 php 的 ODBC 模組
cd ext/pdo_odbc
./configure –with-php-config=/usr/local/php/bin/php-config –with-pdo-odbc=unixODBC,/usr
make
make install
修改 php.ini
新增 extension=pdo_odbc.so
二、Laravel 配置
1. .env
檔案配置
DB_VERTICA_DSN=VerticaDSNunixodbc
DB_VERTICA_HOST=datanode1
DB_VERTICA_PORT=5433
DB_VERTICA_DATABASE=db1
DB_VERTICA_USERNAME=username
DB_VERTICA_PASSWORD=password
2. config/database.php
增加配置
'vertica' => [
'driver' => 'sqlsrv',
'odbc' => true,
'odbc_datasource_name' => env('DB_VERTICA_DSN'),
'host' => env('DB_VERTICA_HOST', 'localhost'),
'port' => env('DB_VERTICA_PORT', '5433'),
'database' => env('DB_VERTICA_DATABASE', 'forge'),
'username' => env('DB_VERTICA_USERNAME', 'forge'),
'password' => env('DB_VERTICA_PASSWORD', ''),
'charset' => 'utf8',
'prefix' => '',
'prefix_indexes' => true,
],
3. 新增 Vertica 類庫
<?php
namespace App\Models\Common;
use Illuminate\Database\Eloquent\Model;
use Exception;
use Illuminate\Database\Query\Builder;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
class VerticaBase extends Model
{
protected $connection = 'vertica';
protected $table;
/**
* 獲取 table
*
* @param string $tableName
* @return Builder
*/
public function table($tableName = '')
{
return $this->getConnection()->table($tableName);
}
/**
* 獲取多條記錄
*
* @param string $sql
* @param string $comment
* @return bool|Collection
*/
public function fetchAll($sql = '', $comment = '')
{
if (!$sql) {
return false;
}
if ($comment) {
$sql = $this->getComment($sql, $comment) . $sql;
}
$data = $this->getConnection()->select($sql);
if ($data) {
$data = object2array($data);
}
return collect($data);
}
/**
* 獲取第一條記錄
*
* @param string $sql
* @param string $comment
* @return bool|Collection
*/
public function fetch($sql = '', $comment = '')
{
if (!$sql) {
return false;
}
if ($comment) {
$sql = $this->getComment($sql, $comment) . $sql;
}
$data = $this->getConnection()->selectOne($sql);
if ($data) {
$data = object2array($data);
}
return collect($data);
}
/**
* 執行 sql
*
* @param string $sql
* @return bool
*/
public function execute($sql = '')
{
if (!$sql) {
return false;
}
try {
return $this->getConnection()->statement($sql);
} catch (Exception $e) {
Log::error('Vertica Execute Failed, SQL: ' . $sql . '. Exception Msg: ' . $e->getMessage());
return false;
}
}
/**
* Drop 表
*
* @param string $tableName
* @return bool
*/
public function dropTable($tableName = '')
{
$drop_sql = "DROP TABLE IF EXISTS {$tableName};";
return $this->execute($drop_sql);
}
/**
* 通過 statement 取消 sql 執行
*
* @param $statement
* @param string $user
* @return bool
*/
public function cancelQueryByStatement($statement, $user = 'rmos')
{
$queryInfo = $this->getSqlInfoWithStatement($statement, $user);
if (!$queryInfo) {
return false;
}
$sessionId = Core::value($queryInfo, 'session_id');
$statementId = Core::value($queryInfo, 'statement_id');
return $this->interruptStatement($sessionId, $statementId);
}
/**
* 根據 statement 獲取當前再執行的 sql
*
* @param $statement
* @param string $user
* @return bool|Collection
*/
public function getSqlInfoWithStatement($statement, $user = 'rmos')
{
$sql = "select session_id,statement_id, current_statement from sessions where user_name='{$user}' "
. " and current_statement not like 'select session_id from sessions%' "
. " and current_statement like '%{$statement}%' limit 1;";
return $this->fetch($sql);
}
/**
* 關閉會話
*
* @param $sessionId
* @return bool
*/
public function closeSession($sessionId)
{
if (!$sessionId) {
return false;
}
$sql = "SELECT CLOSE_SESSION('{$sessionId}');";
return $this->execute($sql);
}
/**
* 取消查詢
*
* @param $sessionId
* @param $statementId
* @return bool
*/
public function interruptStatement($sessionId, $statementId)
{
if (!$sessionId) {
return false;
}
if (!$statementId) {
return $this->closeSession($sessionId);
}
$sql = "SELECT INTERRUPT_STATEMENT('{$sessionId}', '{$statementId}');";
return $this->execute($sql);
}
}
4. 使用方式
$vtc = new \App\Models\Vertica\Vtc();
$vtc->table('table_name')->count();
// 或者
$vtc->fetchAll('select * from table_name limit 10');
// 或者
$vtc->fetch('select * from table_name limit 1');
附錄
1. PDO_ODBC varchar 超過 255 返回 null 的問題
問題描述
Reproduce code:
---------------
create table T (
[A] varchar(80) NOT NULL,
[B] tinyint NOT NULL,
[C] varchar(100) NOT NULL,
[D] smallint NOT NULL,
[E] varchar(1024) NOT NULL,
[F] varchar(255) NOT NULL,
[G] varchar(255) NOT NULL,
[H] varchar(1000) NOT NULL,
[I] varchar(100) NOT NULL,
[J] tinyint NOT NULL,
[K] varchar(255) NULL
)
insert into T values ('A', '1', 'C', '2', 'E', 'F', 'G', 'H', 'I', '3', 'K')
//////////////////////////////////////////////////////////////
try {
$db = new PDO('odbc:Driver={SQL Server};Server=HOST\INSTANCENAME;Database=XXXX;', "user", "pass");
} catch( PDOException $e ){
die( $e->getMessage() );
}
foreach( $db->query("SELECT * FROM T ", PDO::FETCH_NUM) as $row ) {
echo "
<pre>"; print_r( $row );echo "</pre>";
}
Expected result:
----------------
[0] => A
[1] => 1
[2] => C
[3] => 2
[4] => E
[5] => F
[6] => G
[7] => H
[8] => I
[9] => 3
[10] => K
Actual result:
--------------
[0] => A
[1] => 1
[2] => C
[3] => 2
[4] =>
[5] => F
[6] => G
[7] =>
[8] => I
[9] => 3
[10] => K
解決思路
- 經排查此問題是由於 pdo_odbc 擴充套件限制導致的
- 臨時解決思路為調整 pdo_odbc 擴充套件原始碼
解決步驟
- 修改
ext/pdo_odbc/odbc_stmt.c
if (colsize < 256 && !S->going_long) {
...
} else {
...
}
修改為
if (1) {
...
} else {
...
}
- 重新編譯 pdo_odbc 擴充套件
$ /data/modules/php/bin/phpize
$ ./configure --with-php-config=/data/modules/php/bin/php-config --with-pdo-odbc=unixODBC,/usr
$ make && make install
- 過載 php
/etc/inid.t/php-fpm restart
本作品採用《CC 協議》,轉載必須註明作者和本文連結