# 使用ODBC连接
DWS支持使用ODBC应用程序连接数据库。应用程序可以在华为云平台环境的弹性云服务器中，或者互联网环境连接数据库。
ODBC接口的使用方法，请自行查阅官方文档。
#### 前提条件
- 已下载Linux版本的ODBC驱动包**"dws_x.x.x_odbc_driver_for_xxx.zip"** 和Windows版本的ODBC驱动包**"dws_odbc_driver_for_windows.zip"** ，请参见[下载JDBC或ODBC驱动](https://support.huaweicloud.com/mgtg-dws/dws_01_0032.html)。
  DWS也支持开源的ODBC驱动程序：PostgreSQL ODBC 09.01.0200或更高版本。
  
- 已下载开源unixODBC代码文件，支持版本为**2.3.0** ，下载地址：<https://sourceforge.net/projects/unixodbc/files/unixODBC/2.3.0/unixODBC-2.3.0.tar.gz/download>
- 已下载SSL证书文件，请参见[下载SSL证书](https://support.huaweicloud.com/mgtg-dws/dws_01_0038.html#ZH-CN_TOPIC_0000002617505111__li13478842115911)。
 
#### 在Linux环境使用ODBC连接
1. 将ODBC驱动包和代码文件上传到Linux环境，并解压到指定目录。
2. 登录Linux环境。
3. 准备unixODBC。 
   1. 解压unixODBC代码文件。
      ```
      tar -xvf unixODBC-2.3.0.tar.gz
      ```
      
   
   2. 编译并安装。
      ```
      cd unixODBC-2.3.0
      ./configure --enable-gui=no --prefix=[your_path]#如果要在ARM服务器上编译，请追加一个configure参数：--build=aarch64-unknown-linux-gnu
      make
      make install
      ```
      - 其中"\[your_path\]"表示安装路径，可自由指定，需要绝对路径。
      
      - 此时unixODBC编译安装完成后，安装目录下会有\*.so.2的库文件，如果需要编译出\*.so.1的库文件，需要将configure文件中的LIB_VERSION修改为：LIB_VERSION="1:0:0"。
        ```
        LIB_VERSION="1:0:0"
        ```
        
      
      - 在本驱动中，会动态地加载库文件libodbcinst.so.\*，加载成功其中的一个则完成对该库文件的加载。**其中加载的优先级为libodbcinst.so\>libodbcinst.so.1\>libodbcinst.so.1.0.0\>libodbcinst.so.2\>libodbcinst.so.2.0.0** 。
        例如在某目录下可以动态的链接到libodbcinst.so.1、libodbcinst.so.1.0.0、libodbcinst.so.2。驱动文件会先加载libodbcinst.so，如果当前环境中无法找到libodbcinst.so，则会继续寻找优先级低的libodbcinst.so.1，当成功的加载到libodbcinst.so.1后即完成了对该动态链接库的加载。
        
       
   
   
   
   
4. 替换驱动文件（本文以redhat操作系统的包dws_8.1.x_odbc_driver_for_x86_redhat.zip为例）。 
   1. 解压"dws_8.1.x_odbc_driver_for_x86_redhat.zip"。
      ```
      unzip dws_8.1.x_odbc_driver_for_x86_redhat.zip
      ```
      
   
   2. 将"dws_8.1.x_odbc_driver_for_x86_redhat.zip"解压后"lib"目录下所有文件，替换到"\[your_path\]/lib"。
   
   3. 将"dws_8.1.x_odbc_driver_for_x86_redhat.zip"解压后"odbc/lib"目录下的"psqlodbcw.la"和"psqlodbcw.so"，保存到"\[your_path\]/lib/odbc"。
   
   
   
   
5. 执行以下命令，修改驱动文件配置。 
   ```
   vi [your_path]/etc/odbcinst.ini
   ```
   将以下内容保存到配置中：
   ```
   [DWS] 
   Driver64=[your_path]/lib/odbc/psqlodbcw.so
   ```
   参数说明如下：
   - "\[DWS\]"：表示驱动器名称，支持自定义。
   
   - "Driver64"或"Driver"：表示驱动动态库的路径。64位系统优先查找"Driver64"配置项，如果未配置则会继续查找"Driver"。
   
   
   
   
6. 执行以下命令，修改数据源文件。 
   ```
   vi [your_path]/etc/odbc.ini
   ```
   将以下内容保存到配置文件中，并退出修改。
   ```
   [DWSODBC]
   Driver=DWS
   Servername=10.10.0.13
   Database=gaussdb
   Username=dbadmin
   Password=password
   Port=8000
   Sslmode=allow
   ```
   
   | **参数名**     | **说明**                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  | **参数值样例**              |
   |:---|:---|:---|
   | \[DSN\]    | 数据源的名称。                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                | \[DWSODBC\]           |
   | Driver     | 驱动名称，对应"odbcinst.ini"中的DriverName。                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     | Driver=DWS            |
   | Servername | 服务器的IP地址，当集群绑定弹性负载均衡(ELB)时，应设置为ELB的IP地址。                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               | Servername=10.10.0.13 |
   | Database   | 要连接的数据库的名称。                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            | Database=gaussdb      |
   | Username   | 数据库用户名称。                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               | Username=dbadmin      |
   | Password   | 数据库用户密码。                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               | Password=*password*    |
   | Port        | 服务器的端口号。                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               | Port=8000              |
   | Sslmode    | SSL认证工作模式。集群默认开启。SSL模式安全性高于普通模式，集群默认开启SSL功能允许来自客户端的SSL连接或非SSL连接，建议在使用ODBC连接DWS集群时采用SSL模式。 取值及含义： - disable：只尝试非SSL连接。  - allow：首先尝试非SSL连接，如果连接失败，再尝试SSL连接。  - prefer：首先尝试SSL连接，如果连接失败，将尝试非SSL连接。  - require：只尝试SSL连接。如果存在CA文件，则按设置成verify-ca的方式验证。  - verify-ca：只尝试SSL连接，并且验证服务器是否具有由可信任的证书机构签发的证书。  - verify-full：DWS不支持此模式。   | Sslmode=allow          |
      
   - 其中，参数"Servername"和"Port"的值，可以DWS的管理控制台查看。请[登录DWS管理控制台](https://console.huaweicloud.com/dws)，选择"集群 \> 管理 \> 连接客户端"，在"数据仓库连接字符串"区域，选择指定的集群，获取该集群的"内网访问地址"或"公网访问地址"。具体步骤请参见[获取DWS集群连接地址](https://support.huaweicloud.com/mgtg-dws/dws_01_0033.html)。
   
   
   
   
7. 配置环境变量。 
   ```
   vi ~/.bashrc
   ```
   在配置文件中追加以下内容。麒麟OS环境中，此处不建议追加LD_LIBRARY_PATH，可能造成libssl.so库冲突。集群8.2.1和9.1.0最新版本中已增加rpath，不需要LD_LIBRARY_PATH即可找到依赖。
   ```
   export LD_LIBRARY_PATH=[your_path]/lib/:$LD_LIBRARY_PATH 
   export ODBCSYSINI=[your_path]/etc 
   export ODBCINI=[your_path]/etc/odbc.ini
   ```
   
   
8. 导入环境变量。 
   ```
   source ~/.bashrc
   ```
   
   
9. 执行以下命令，开始连接。 
   ```
   [your_path]/bin/isql -v DWSODBC
   ```
   界面显示以下信息表示连接成功：
   ```
   +---------------------------------------+ 
   | Connected!                            | 
   |                                       | 
   | sql-statement                         | 
   | help [tablename]                      | 
   | quit                                  | 
   |                                       | 
   +---------------------------------------+  
   SQL>
   ```
   
   
 
#### 在Windows环境使用ODBC连接
1. 解压Windows版本的ODBC驱动包"dws_odbc_driver_for_windows.zip"，并安装"psqlodbc.msi"。
2. 解压SSL证书压缩包，并准备证书文件。 
   用户可以根据实际情况选择自动或手动部署方法：
   - 自动部署： 双击"sslcert_env.bat"文件，即可完成证书的默认位置的部署。该sslcert_env.bat为了保证证书环境的纯净，在%APPDATA%\\postgresql目录存在时，会提示是否需要移除相关目录。如果有需要，请备份该目录中的文件。
     
   
   - 手动部署：
     - 在"%APPDATA%\\"目录创建一个新文件夹，并命名为"postgresql"。
     
     - 将证书文件包中的"client.crt"、"client.key"、"client.key.cipher"、"client.key.rand"文件保存至"%APPDATA%\\postgresql"目录，并且将文件名中的**client** 改为**postgres**，例如"client.key"修改为"postgres.key"。
     
     - 将"cacert.pem"文件保存至"%APPDATA%\\postgresql"目录，并更名为"root.crt"。
      
   
   
   
   
3. 打开驱动管理器。 
   因为目前DWS提供了32位和64位的ODBC驱动程序，用户可根据自身需求选择；在配置数据源时，请使用对应的驱动管理器（假设操作系统安装盘符为C:盘，如果是其他盘符，请对路径做相应修改）：
   - 64位操作系统上进行**32** 位程序开发，安装**32** 位程序驱动后。使用**32** 位的驱动管理器：C:\\Windows\\**SysWOW64** \\odbcad32.exe（WoW64的全称是"Windows 32-bit on Windows 64-bit"，C:\\Windows\\SysWOW64\\存放的是64位系统上的32位运行环境。）
     请勿直接使用"控制面板\> 管理工具 \> 数据源（ODBC）"。
     
   
   - 64操作系统上进行**64** 位程序开发，安装**64** 位驱动程序后，使用**64** 位的驱动管理器：C:\\Windows\\**System32** \\odbcad32.exe（C:\\Windows\\System32\\存放的是与操作系统一致的运行环境，具体的技术信息请查阅Windows的相关技术文档。）
     请勿直接使用"控制面板 \> 管理工具\> 数据源（ODBC）"。
     
   
   - 32位操作系统请使用：C:\\Windows\\**System32** \\odbcad32.exe
     或者单击"计算机 \> 控制面板 \> 管理工具 \> 数据源（ODBC）"打开驱动管理器。
     
   
   
   
   
4. 配置连接数据源。 
   1. 在打开的驱动管理器上，选择"用户DSN \> 添加 \> PostgreSQL Unicode"，然后进行配置。
      图1配置连接数据源   
      ![](https://support.huaweicloud.com/mgtg-dws/figure/zh-cn_image_0000002587065870.png)
      其中，配置项"Server"和"Port"的值，可以在DWS的管理控制台查看。请[登录DWS管理控制台](https://console.huaweicloud.com/dws)，选择"集群 \> 管理 \> 连接客户端"，在"数据仓库连接字符串"区域，选择指定的集群，获取该集群的"内网访问地址"或"公网访问地址"。具体步骤请参见[获取DWS集群连接地址](https://support.huaweicloud.com/mgtg-dws/dws_01_0033.html)。
      
   
   2. 单击"Test"验证连接正确，界面提示"Connection successful"。
   
   
   
   
5. 编写ODBC样例程序连接数据源。 
   ODBC接口不提供重试连接数据库的能力，您需要在业务代码中实现重试连接的处理。
   样例代码如下：
   ```
   // 此示例演示如何通过ODBC方式获取DWS中的数据。
   // DBtest.c (compile with: libodbc.so)  
   #include <stdlib.h> 
   #include <stdio.h> 
   #include <sqlext.h>
   #ifdef WIN32
   #include <windows.h>
   #endif 
   SQLHENV       V_OD_Env;        // Handle ODBC environment 
   SQLHSTMT      V_OD_hstmt;      // Handle statement 
   SQLHDBC       V_OD_hdbc;       // Handle connection     
   char          typename[100];
   SQLINTEGER    value = 100;
   SQLINTEGER    V_OD_erg,V_OD_buffer,V_OD_err,V_OD_id;
   int main(int argc,char *argv[]) 
   {         
         // 1. 申请环境句柄       
         V_OD_erg = SQLAllocHandle(SQL_HANDLE_ENV,SQL_NULL_HANDLE,&V_OD_Env);     
         if ((V_OD_erg != SQL_SUCCESS) && (V_OD_erg != SQL_SUCCESS_WITH_INFO))        
         {           
              printf("Error AllocHandle\n");           
              exit(0);        
         } 
         // 2. 设置环境属性（版本信息）         
         SQLSetEnvAttr(V_OD_Env, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3, 0);      
         // 3. 申请连接句柄        
         V_OD_erg = SQLAllocHandle(SQL_HANDLE_DBC, V_OD_Env, &V_OD_hdbc);     
         if ((V_OD_erg != SQL_SUCCESS) && (V_OD_erg != SQL_SUCCESS_WITH_INFO))      
         {                     
              SQLFreeHandle(SQL_HANDLE_ENV, V_OD_Env);          
              exit(0);       
         }
         // 4. 设置连接属性
         SQLSetConnectAttr(V_OD_hdbc, SQL_ATTR_AUTOCOMMIT, SQL_AUTOCOMMIT_ON, 0);          
         // 5. 连接数据源，其中的用户名和用户密码等信息之所以能省略，是因为在odbc.ini文件中进行了配置，若没配置需要在SQLConnect函数参数中具体写明要连接数据库的用户名和用户密码等信息。      
         V_OD_erg = SQLConnect(V_OD_hdbc, (SQLCHAR*) "gaussdb", SQL_NTS,  
                              (SQLCHAR*) "", SQL_NTS,  (SQLCHAR*) "", SQL_NTS);        
         if ((V_OD_erg != SQL_SUCCESS) && (V_OD_erg != SQL_SUCCESS_WITH_INFO))      
         {           
             printf("Error SQLConnect %d\n",V_OD_erg);            
             SQLFreeHandle(SQL_HANDLE_ENV, V_OD_Env);       
             exit(0);        
         }     
         printf("Connected !\n"); 
         // 6. 设置语句属性
         SQLSetStmtAttr(V_OD_hstmt,SQL_ATTR_QUERY_TIMEOUT,(SQLPOINTER *)3,0);
         // 7. 申请语句句柄
         SQLAllocHandle(SQL_HANDLE_STMT, V_OD_hdbc, &V_OD_hstmt);       
         // 8. 直接执行SQL语句。
         SQLExecDirect(V_OD_hstmt,"drop table IF EXISTS testtable",SQL_NTS);
         SQLExecDirect(V_OD_hstmt,"create table testtable(id int)",SQL_NTS);
         SQLExecDirect(V_OD_hstmt,"insert into testtable values(25)",SQL_NTS);
         // 9. 准备执行
         SQLPrepare(V_OD_hstmt,"insert into testtable values(?)",SQL_NTS); 
         // 10. 绑定参数
         SQLBindParameter(V_OD_hstmt,1,SQL_PARAM_INPUT,SQL_C_SLONG,SQL_INTEGER,0,0,
                          &value,0,NULL);
         // 11. 执行准备好的语句
         SQLExecute(V_OD_hstmt);
         SQLExecDirect(V_OD_hstmt,"select c_customer_sk from customer_t1",SQL_NTS);
         // 12. 获取结果集某一列的属性
         SQLColAttribute(V_OD_hstmt,1,SQL_DESC_TYPE,typename,100,NULL,NULL);                 
         printf("SQLColAttribute %s\n",typename);
         // 13. 绑定结果集
         SQLBindCol(V_OD_hstmt,1,SQL_C_SLONG, (SQLPOINTER)&V_OD_buffer,150,
                   (SQLLEN *)&V_OD_err);
         // 14. 通过SQLFetch取结果集中数据
         V_OD_erg=SQLFetch(V_OD_hstmt);
         // 15. 通过SQLGetData获取并返回数据。
         while(V_OD_erg != SQL_NO_DATA)
         {
             SQLGetData(V_OD_hstmt,1,SQL_C_SLONG,(SQLPOINTER)&V_OD_id,0,NULL);
             printf("SQLGetData ----ID = %d\n",V_OD_id);
             V_OD_erg=SQLFetch(V_OD_hstmt);
         };
         printf("Done !\n");
         // 16. 断开数据源连接并释放句柄资源
         SQLFreeHandle(SQL_HANDLE_STMT,V_OD_hstmt);    
         SQLDisconnect(V_OD_hdbc);         
         SQLFreeHandle(SQL_HANDLE_DBC,V_OD_hdbc);       
         SQLFreeHandle(SQL_HANDLE_ENV, V_OD_Env);  
         return(0);
    }
   ```
   
   
 
