目录结构
注:提前言明 本文借鉴了以下博主、书籍或网站的内容,其列表如下:
1、参考书籍:《Oracle Database SQL Language Reference》
2、参考书籍:《PostgreSQL中文手册》
3、EDB Postgres Advanced Server User Guides,点击前往
4、PostgreSQL数据库仓库链接,点击前往
5、PostgreSQL中文社区,点击前往
6、Microsoft 开放式数据库连接 (ODBC),点击前往
7、ODBC 程序员参考,点击前往
8、ODBC 概述,点击前往
1、本文内容全部来源于开源社区 GitHub和以上博主的贡献,本文也免费开源(可能会存在问题,评论区等待大佬们的指正)
2、本文目的:开源共享 抛砖引玉 一起学习
3、本文不提供任何资源 不存在任何交易 与任何组织和机构无关
4、大家可以根据需要自行 复制粘贴以及作为其他个人用途,但是不允许转载 不允许商用 (写作不易,还请见谅 💖)
ODBC开放式数据库连接概述
- 文章快速说明索引
- 开放式数据库连接
- 如何开发应用程序
- 建立 ODBC DSN
- 编写 ODBC 程序
- 编写 OCI 程序
文章快速说明索引
学习目标:
目的:接下来这段时间我想做一些兼容Oracle数据库Real Application Testing (即:RAT)上的一些功能开发,本专栏这里主要是学习以及介绍Oracle数据库功能的使用场景、原理说明和注意事项等,基于PostgreSQL数据库的功能开发等之后 由新博客进行介绍和分享!
学习内容:(详见目录)
1、ODBC开放式数据库连接概述
学习时间:
2023年12月17日 14:07:20
学习产出:
1、ODBC开放式数据库连接概述
2、CSDN 技术博客 1篇
注:下面我们所有的学习环境是Centos7+PostgreSQL16.1+Oracle19c+MySQL5.7
postgres=# select version();version
---------------------------------------------------------------------------------------------------------PostgreSQL 16.1 on x86_64-pc-linux-gnu, compiled by gcc (GCC) 8.5.0 20210514 (Red Hat 8.5.0-15), 64-bit
(1 row)postgres=##-----------------------------------------------------------------------------#SQL> select * from v$version; BANNER BANNER_FULL BANNER_LEGACY CON_ID
--------------------------------------------------------------------------- --------------------------------------------------------------------------- --------------------------------------------------------------------------- ----------
Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production 0Version 19.3.0.0.0SQL>
#-----------------------------------------------------------------------------#mysql> select version();
+-----------+
| version() |
+-----------+
| 5.7.19 |
+-----------+
1 row in set (0.06 sec)mysql>
开放式数据库连接
开放式数据库连接 (ODBC
) 是一种广泛接受的应用程序编程接口 (API),适用于数据库访问。其基于 Open Group 和 ISO/IEC 的数据库 API 调用级别接口 (CLI) 规范,并使用结构化查询语言 (SQL) 作为其数据库访问语言。
ODBC 旨在实现最大的互操作性,即单个应用程序能够使用相同的源代码访问不同的数据库管理系统 (DBMS)。数据库应用程序会在 ODBC 接口中调用函数,这些函数在特定于数据库的模块(称为“驱动程序”)中实现。驱动程序的使用将应用程序与特定于数据库的调用隔离,就像打印机驱动程序将字处理程序与特定于打印机的命令隔离一样。由于驱动程序在运行时加载,因此用户需要添加新驱动程序才能访问新的 DBMS;无需重新编译或重新链接应用程序。
ODBC
是为客户应用程序访问关系数据库时提供的一个标准的接口,对于不同的数据库,ODBC
提供了统一的 API
,使应用程序调用提供的 API
来访问任何提供了 ODBC
驱动程序的数据库:
- 应用程序(Application,即对应下图的客户程序):应用程序本身不直接与数据库打交道,主要负责处理并调用
ODBC
函数,发送对数据库的SQL
请求及获取结果 - 驱动程序管理器(
Driver Manager
,即对应下图的ODBC
驱动管理程序):驱动程序管理器是一个带有输入程序的动态链接库(DLL
),主要目的是加载驱动程序,处理ODBC
调用的初始化调用,提供ODBC
调用的参数有效性和序列有效性 - 驱动程序(
Driver
,即对应下图的ODBC
驱动程序):驱动程序是一个完成ODBC
函数调用并与数据库相互影响的DLL
,这些驱动程序可以处理对于特定的数据的数据库访问请求。对于应用驱动程序管理器送来的命令,驱动程序再进行解释形成自己的数据库所能理解的命令。驱动程序将处理所有的数据库访问请求,对于应用程序来讲不需要关注所使用的是本地数据库还是网络数据库
总结一下,ODBC 体系结构有四个主要组成部分:
- 应用程序:执行处理并调用 ODBC 函数来提交 SQL 语句并检索结果
- 驱动程序管理器:代表应用程序加载和卸载驱动程序。 处理 ODBC 函数调用或将其传递给驱动程序
- 驱动程序:处理 ODBC 函数调用,将 SQL 请求提交到特定数据源,并将结果返回到应用程序。 如有必要,驱动程序会修改应用程序的请求,以便该请求符合关联的 DBMS 支持的语法
- 数据源:由用户想要访问的数据及其关联的操作系统、DBMS 和用于访问 DBMS 的网络平台(如果有)组成
如何开发应用程序
一个ODBC应用程序的编写,通常步骤如下所示:
任何应用程序都不太可能完全按此顺序调用所有这些函数。但是,大多数应用程序使用这些步骤的一些变体。下图显示了基本的应用程序步骤:
简单解释一下,例如:
- 步骤 1:连接数据源
- 步骤 2:初始化应用程序
- 步骤 3:生成并执行 SQL 语句
- 步骤 4a:提取结果;步骤 4b:提取行计数
- 步骤 5:提交事务
- 步骤 6:从数据源断开连接
建立 ODBC DSN
DSN(Data Source Name
)是用于指定ODBC与相关的驱动程序相对应的一个入口,所有DSN的信息由系统进行管理。一般来讲当应用程序要使用ODBC访问数据库时,就需要指定一个DSN以便于连接到一个指定的ODBC驱动程序。
下面列出了 ODBC Driver for SQL Server 中提供的连接字符串和 DSN 的关键字以及 SQLSetConnectAttr 和 SQLGetConnectAttr 的连接属性:
- 受支持的 DSN/连接字符串关键字和连接属性
我们这里以连接oracle数据库为例,编写一个odbc应用测试程序。作为服务端的Oracle数据库 机器1如下:
[oracle@dbserver ~]$ ifconfig
ens33: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500inet 192.168.1.14 netmask 255.255.255.0 broadcast 192.168.1.255inet6 2409:8a71:abc:ed70:250:56ff:fe2b:6b00 prefixlen 64 scopeid 0x0<global>inet6 fe80::250:56ff:fe2b:6b00 prefixlen 64 scopeid 0x20<link>ether 00:50:56:2b:6b:00 txqueuelen 1000 (Ethernet)RX packets 3711 bytes 2060244 (1.9 MiB)RX errors 0 dropped 5 overruns 0 frame 0TX packets 1374 bytes 168460 (164.5 KiB)TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536inet 127.0.0.1 netmask 255.0.0.0inet6 ::1 prefixlen 128 scopeid 0x10<host>loop txqueuelen 1000 (Local Loopback)RX packets 415 bytes 64588 (63.0 KiB)RX errors 0 dropped 0 overruns 0 frame 0TX packets 415 bytes 64588 (63.0 KiB)TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0virbr0: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500inet 192.168.122.1 netmask 255.255.255.0 broadcast 192.168.122.255ether 52:54:00:3b:26:0a txqueuelen 1000 (Ethernet)RX packets 0 bytes 0 (0.0 B)RX errors 0 dropped 0 overruns 0 frame 0TX packets 0 bytes 0 (0.0 B)TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0[oracle@dbserver ~]$
[oracle@dbserver ~]$ sqlplus / as sysdbaSQL*Plus: Release 19.0.0.0.0 - Production on Tue Dec 19 09:57:49 2023
Version 19.3.0.0.0Copyright (c) 1982, 2019, Oracle. All rights reserved.Connected to an idle instance.SQL> startup;
ORACLE instance started.Total System Global Area 1157624440 bytes
Fixed Size 9134712 bytes
Variable Size 352321536 bytes
Database Buffers 788529152 bytes
Redo Buffers 7639040 bytes
Database mounted.
Database opened.
SQL>
SQL> select * from v$version;BANNER BANNER_FULL BANNER_LEGACY CON_ID
--------------------------------------------------------------------------- --------------------------------------------------------------------------- --------------------------------------------------------------------------- ----------
Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production 0Version 19.3.0.0.0SQL>
机器2(CentOS8)配置ODBC连接oracle,步骤如下:
一、安装oracle客户端及oracle ODBC驱动程序(这里一定要注意别下错了,32位还是64位)
// 下载oracle客户端包:https://www.oracle.com/cn/database/technologies/instant-client/linux-x86-64-downloads.html
https://download.oracle.com/otn_software/linux/instantclient/1921000/oracle-instantclient19.21-basic-19.21.0.0.0-1.x86_64.rpmhttps://download.oracle.com/otn_software/linux/instantclient/1921000/oracle-instantclient19.21-sqlplus-19.21.0.0.0-1.x86_64.rpmhttps://download.oracle.com/otn_software/linux/instantclient/1921000/oracle-instantclient19.21-tools-19.21.0.0.0-1.x86_64.rpmhttps://download.oracle.com/otn_software/linux/instantclient/1921000/oracle-instantclient19.21-devel-19.21.0.0.0-1.x86_64.rpmhttps://download.oracle.com/otn_software/linux/instantclient/1921000/oracle-instantclient19.21-odbc-19.21.0.0.0-1.x86_64.rpm
提前安装相关依赖,如下:
[postgres@localhost:~/odbcloc]$ sudo yum install libaio*
...
[postgres@localhost:~/odbcloc]$ sudo yum install libnsl*
...
[postgres@localhost:~/odbcloc]$ sudo yum install libaio.so.1* -y
...
[postgres@localhost:~/odbcloc]$ sudo yum install libnsl.so.1* -y
...
[root@localhost odbc]# ll
total 54924
-rw-rw-r--. 1 postgres postgres 53832016 Dec 19 00:17 oracle-instantclient19.21-basic-19.21.0.0.0-1.x86_64.rpm
-rw-rw-r--. 1 postgres postgres 614204 Dec 19 00:17 oracle-instantclient19.21-devel-19.21.0.0.0-1.x86_64.rpm
-rw-rw-r--. 1 postgres postgres 246728 Dec 19 00:17 oracle-instantclient19.21-odbc-19.21.0.0.0-1.x86_64.rpm
-rw-rw-r--. 1 postgres postgres 703388 Dec 19 00:17 oracle-instantclient19.21-sqlplus-19.21.0.0.0-1.x86_64.rpm
-rw-rw-r--. 1 postgres postgres 837556 Dec 19 00:17 oracle-instantclient19.21-tools-19.21.0.0.0-1.x86_64.rpm
[root@localhost odbc]#
[root@localhost odbc]# rpm -ivh oracle-instantclient19.21-basic-19.21.0.0.0-1.x86_64.rpm
Verifying... ################################# [100%]
Preparing... ################################# [100%]
Updating / installing...1:oracle-instantclient19.21-basic-1################################# [100%]
[root@localhost odbc]#
[root@localhost odbc]# rpm -ivh oracle-instantclient19.21-sqlplus-19.21.0.0.0-1.x86_64.rpm
Verifying... ################################# [100%]
Preparing... ################################# [100%]
Updating / installing...1:oracle-instantclient19.21-sqlplus################################# [100%]
[root@localhost odbc]#
[root@localhost odbc]# rpm -ivh oracle-instantclient19.21-devel-19.21.0.0.0-1.x86_64.rpm
Verifying... ################################# [100%]
Preparing... ################################# [100%]
Updating / installing...1:oracle-instantclient19.21-devel-1################################# [100%]
[root@localhost odbc]#
[root@localhost odbc]# rpm -ivh oracle-instantclient19.21-tools-19.21.0.0.0-1.x86_64.rpm
Verifying... ################################# [100%]
Preparing... ################################# [100%]
Updating / installing...1:oracle-instantclient19.21-tools-1################################# [100%]
[root@localhost odbc]#
[root@localhost odbc]# rpm -ivh oracle-instantclient19.21-odbc-19.21.0.0.0-1.x86_64.rpm
Verifying... ################################# [100%]
Preparing... ################################# [100%]
Updating / installing...1:oracle-instantclient19.21-odbc-19################################# [100%]
[root@localhost odbc]#
执行如下命令查看rpm安装后,生成文件及安装路径:
[root@localhost odbc]# rpm -qpl oracle-instantclient19.21-odbc-19.21.0.0.0-1.x86_64.rpm
/usr/lib/oracle
/usr/lib/oracle/19.21
/usr/lib/oracle/19.21/client64
/usr/lib/oracle/19.21/client64/bin
/usr/lib/oracle/19.21/client64/bin/odbc_update_ini.sh
/usr/lib/oracle/19.21/client64/lib
/usr/lib/oracle/19.21/client64/lib/libsqora.so.19.1
/usr/share/oracle
/usr/share/oracle/19.21
/usr/share/oracle/19.21/client64
/usr/share/oracle/19.21/client64/doc
/usr/share/oracle/19.21/client64/doc/ODBC_LICENSE
/usr/share/oracle/19.21/client64/doc/ODBC_README
[root@localhost odbc]#
配置环境变量,如下:
[root@localhost odbcloc]# vim /etc/profile
...
#根据实际安装路径配置export ORACLE_HOME=/usr/lib/oracle/19.21/client64export TNS_ADMIN=/usr/lib/oracle/19.21/client64/network/adminexport LD_LIBRARY_PATH=/usr/lib/oracle/19.21/client64/libexport ORACLE_SID=orclexport PATH=$ORACLE_HOME:$PATHexport PATH=$PATH:$HOME/bin:$ORACLE_HOME/bin
source /etc/profile
使其生效
配置监听配置文件,如下:
[postgres@localhost:~/odbc]$ sudo mkdir -p /usr/lib/oracle/19.21/client64/network/admin
[sudo] password for postgres:
[postgres@localhost:~/odbc]$
[postgres@localhost:~/odbc]$ cd /usr/lib/oracle/19.21/client64/network/admin/
[postgres@localhost:/usr/lib/oracle/19.21/client64/network/admin]$
[postgres@localhost:/usr/lib/oracle/19.21/client64/network/admin]$ vim tnsnames.ora
[postgres@localhost:/usr/lib/oracle/19.21/client64/network/admin]$
[postgres@localhost:/usr/lib/oracle/19.21/client64/network/admin]$ sudo vim tnsnames.ora
[postgres@localhost:/usr/lib/oracle/19.21/client64/network/admin]$
[postgres@localhost:/usr/lib/oracle/19.21/client64/network/admin]$ cat tnsnames.ora
ORCL =
(DESCRIPTION =(ADDRESS_LIST =(ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.1.14)(PORT = 1521)))(CONNECT_DATA =(SERVICE_NAME = ORCL))
)[postgres@localhost:/usr/lib/oracle/19.21/client64/network/admin]$
二、安装 unixODBC 驱动程序管理器,如下:
[root@localhost admin]# yum install unixODBC unixODBC-devel -y
...
若是选择源码编译安装,则获取 unixODBC-2.3.1.tar.gz
。 可以从 http://www.unixodbc.org
获取 unixODBC-2.3.1.tar.gz
放到/usr/local
下,然后运行下述命令:
注:如果自定义其他安装目录,需要配置环境变量,否则使用时可能会出现找不到库的问题
tar zxvf unixODBC-2.3.1.tar.gz
cd unixODBC-2.3.1
./configure --prefix=/usr/local/unixODBC-2.3.1 --includedir=/usr/include
--libdir=/usr/lib -bindir=/usr/bin --sysconfdir=/etc
make
make install
三、安装之后运行odbcinst -j
,如下:
[postgres@localhost:~]$ odbcinst -j
unixODBC 2.3.7
DRIVERS............: /etc/odbcinst.ini
SYSTEM DATA SOURCES: /etc/odbc.ini
FILE DATA SOURCES..: /etc/ODBCDataSources
USER DATA SOURCES..: /home/postgres/.odbc.ini
SQLULEN Size.......: 8
SQLLEN Size........: 8
SQLSETPOSIROW Size.: 8
[postgres@localhost:~]$
根据odbcinst -j
显示的路径,配置 odbcinst.ini
和 odbc.ini
文件
[postgres@localhost:~]$ sudo vim /etc/odbcinst.ini
[postgres@localhost:~]$
[postgres@localhost:~]$ cat /etc/odbcinst.ini
# Example driver definitions# Driver from the postgresql-odbc package
# Setup from the unixODBC package
[PostgreSQL]
Description = ODBC for PostgreSQL
Driver = /usr/lib/psqlodbcw.so
Setup = /usr/lib/libodbcpsqlS.so
Driver64 = /usr/lib64/psqlodbcw.so
Setup64 = /usr/lib64/libodbcpsqlS.so
FileUsage = 1[ORACLE]
Description = ODBC for Oracle
Driver = /usr/lib/oracle/19.21/client64/lib/libsqora.so.19.1
FileUsage = 1[postgres@localhost:~]$
[postgres@localhost:~]$ ls /usr/lib/oracle/19.21/client64/lib/libsqora.so.19.1
/usr/lib/oracle/19.21/client64/lib/libsqora.so.19.1
[postgres@localhost:~]$
[postgres@localhost:~]$ ldd /usr/lib/oracle/19.21/client64/lib/libsqora.so.19.1linux-vdso.so.1 (0x00007ffc2750b000)libdl.so.2 => /lib64/libdl.so.2 (0x00007f0dec769000)libm.so.6 => /lib64/libm.so.6 (0x00007f0dec3e7000)libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f0dec1c7000)libnsl.so.1 => /lib64/libnsl.so.1 (0x00007f0debfae000)librt.so.1 => /lib64/librt.so.1 (0x00007f0debda6000)libaio.so.1 => /lib64/libaio.so.1 (0x00007f0debba3000)libresolv.so.2 => /lib64/libresolv.so.2 (0x00007f0deb98b000)libclntsh.so.19.1 => /usr/lib/oracle/19.21/client64/lib/libclntsh.so.19.1 (0x00007f0de777c000)libclntshcore.so.19.1 => /usr/lib/oracle/19.21/client64/lib/libclntshcore.so.19.1 (0x00007f0de71d7000)libodbcinst.so.2 => /lib64/libodbcinst.so.2 (0x00007f0de6fc1000)libc.so.6 => /lib64/libc.so.6 (0x00007f0de6bfc000)/lib64/ld-linux-x86-64.so.2 (0x00007f0decc27000)libnnz19.so => /usr/lib/oracle/19.21/client64/lib/libnnz19.so (0x00007f0de6583000)libltdl.so.7 => /lib64/libltdl.so.7 (0x00007f0de6379000)
[postgres@localhost:~]$
在机器2上先行验证一下,如下:
[postgres@localhost:~]$ sqlplus c##oracle/123456@192.168.1.14:1521/orclSQL*Plus: Release 19.0.0.0.0 - Production on Mon Dec 18 18:54:13 2023
Version 19.20.0.0.0Copyright (c) 1982, 2022, Oracle. All rights reserved.Last Successful login time: Mon Dec 18 2023 18:53:33 -08:00Connected to:
Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production
Version 19.3.0.0.0SQL> show user;
USER is "C##ORACLE"
SQL>
SQL> Disconnected from Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production
Version 19.3.0.0.0
[postgres@localhost:~]$
[postgres@localhost:~]$ sudo vim /etc/odbc.ini
[postgres@localhost:~]$
[postgres@localhost:~]$ cat /etc/odbc.ini
[pg]
Description = Test to pg
Driver = PostgreSQL
Database = postgres
Servername = 127.0.0.1
UserName = postgres
Password = 1
Port = 5432
ReadOnly = 0
ConnSettings = set client_encoding to UTF8[oracle_test]
driver = ORACLE
server = 192.168.1.14
port = 1521
ServerName = orcl
UserID = c##oracle
password = 123456[postgres@localhost:~]$
连接测试
[postgres@localhost:~]$ isql -v oracle_test
+---------------------------------------+
| Connected! |
| |
| sql-statement |
| help [tablename] |
| quit |
| |
+---------------------------------------+
SQL> create table t_a(id int, name varchar(64));
SQLRowCount returns -1
SQL> insert into t_a values(1, 'a');
SQLRowCount returns 1
SQL> insert into t_a values(2, 'b');
SQLRowCount returns 1
SQL> insert into t_a values(3, 'c');
SQLRowCount returns 1
SQL> insert into t_a values(4, 'd');
SQLRowCount returns 1
SQL> insert into t_a values(5, 'jj');
SQLRowCount returns 1
SQL> select * from t_a;
+-----------------------------------------+-----------------------------------------------------------------+
| ID | NAME |
+-----------------------------------------+-----------------------------------------------------------------+
| 1 | a |
| 2 | b |
| 3 | c |
| 4 | d |
| 5 | jj |
+-----------------------------------------+-----------------------------------------------------------------+
SQLRowCount returns -1
5 rows fetched
SQL>
编写 ODBC 程序
#include <stdio.h>
#include <sqlext.h>int main() {SQLHENV env; // ODBC环境句柄SQLHDBC dbc; // ODBC连接句柄SQLRETURN ret;// Step 1: 初始化环境ret = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &env);ret = SQLSetEnvAttr(env, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3, SQL_IS_INTEGER);// Step 2: 分配连接句柄ret = SQLAllocHandle(SQL_HANDLE_DBC, env, &dbc);// Step 3: 建立连接ret = SQLDriverConnect(dbc, NULL,(SQLCHAR*)"DRIVER={Oracle};SERVER=<server>;DATABASE=<database>;UID=<username>;PWD=<password>;",SQL_NTS, NULL, 0, NULL, SQL_DRIVER_COMPLETE);if (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO) {printf("Connected to Oracle.\n");// Step 4: 执行SQL查询SQLHSTMT stmt; // SQL语句句柄ret = SQLAllocHandle(SQL_HANDLE_STMT, dbc, &stmt);ret = SQLExecDirect(stmt, (SQLCHAR*)"SELECT * FROM my_table", SQL_NTS);if (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO) {// 处理结果集// ...printf("SQL executed successfully.\n");} else {// 处理错误// ...printf("SQL execution failed.\n");}// Step 5: 释放语句句柄ret = SQLFreeHandle(SQL_HANDLE_STMT, stmt);} else {// 处理连接失败// ...printf("Failed to connect to Oracle.\n");}// Step 6: 断开连接ret = SQLDisconnect(dbc);// Step 7: 释放连接句柄ret = SQLFreeHandle(SQL_HANDLE_DBC, dbc);// Step 8: 释放环境句柄ret = SQLFreeHandle(SQL_HANDLE_ENV, env);return 0;
}
要编译以上的代码,首先需要确保已经安装了ODBC驱动和相关的开发库。接下来,可以使用gcc或其他支持C编译的工具进行编译。例如,使用gcc命令编译该代码:
gcc -o odbc_demo odbc_demo.c -lodbc
gcc -o odbc_demo odbc_demo.c -L/odbc库的路径 -lodbc
#可通过下面的命令查找odbc库的路径
sudo find / -name "libodbc*"
其中,odbc_demo
为输出的可执行文件名,odbc_demo.c
为上述代码保存的文件名。-lodbc
用于链接ODBC库。请注意,编译时可能需要配置包含路径和连接选项,具体取决于所使用的操作系统和ODBC驱动程序。
下面是一个示例,源码odbctest.c
如下:
#include <stdio.h>#include <sql.h>
#include <sqlext.h>static void
test_SQLConnect()
{SQLRETURN ret;SQLHENV env;SQLHDBC conn;HSTMT hstmt = SQL_NULL_HSTMT;SQLSMALLINT sdwNative;SQLINTEGER swMsgLen;char buffer[256];char message[1000];SQLCHAR *dsn = (SQLCHAR *) "oracle_test";//CHAR* queryStr="create table t_odbc(id int);";SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &env);SQLSetEnvAttr(env, SQL_ATTR_ODBC_VERSION, (void *) SQL_OV_ODBC3, 0);SQLAllocHandle(SQL_HANDLE_DBC, env, &conn);printf("Connecting with SQLConnect...\n");ret = SQLConnect(conn, dsn, SQL_NTS, NULL, 0, NULL, 0);if (SQL_SUCCEEDED(ret)) {printf("connected success!\n");} else {printf("SQLConnect failed.\n");return;}ret = SQLSetConnectAttr(conn,SQL_ATTR_AUTOCOMMIT,(SQLPOINTER)SQL_AUTOCOMMIT_OFF,SQL_IS_UINTEGER);if (SQL_SUCCEEDED(ret)) {printf("set autommit off success!\n");} else {printf("set autommit failed.\n");return;}ret = SQLAllocHandle(SQL_HANDLE_STMT, conn, &hstmt);ret = SQLExecDirect(hstmt, (SQLCHAR *)"create table t_odbc(id int)", SQL_NTS);if (SQL_SUCCEEDED(ret)) {printf("create table success!\n");}else {printf("create table failed.\n");return;}ret = SQLExecDirect(hstmt, (SQLCHAR *) "INSERT INTO t_odbc VALUES (10000)", SQL_NTS);if (SQL_SUCCEEDED(ret)) {printf("insert table success!\n");}else {printf("insert table failed.\n");return;}SQLEndTran(SQL_HANDLE_DBC, conn, SQL_COMMIT);if (SQL_SUCCEEDED(ret)) {printf("SQLEndTran success!\n");}else {printf("SQLEndTran failed.\n");return;}ret = SQLFreeHandle(SQL_HANDLE_STMT, hstmt);if (SQL_SUCCEEDED(ret)) {printf("SQLFreeHandle success!\n");}else {printf("SQLFreeHandle failed.\n");return;}ret = SQLDisconnect(conn);if (SQL_SUCCEEDED(ret)) {printf("SQLDisconnect success!\n");}else {printf("SQLDisconnect failed.\n");SQLError(env, conn, hstmt, buffer, &swMsgLen,message, 256, &sdwNative);printf("%s\n%s\n", buffer, message);return;}ret = SQLFreeHandle(SQL_HANDLE_DBC, conn);if (!SQL_SUCCEEDED(ret)){printf("SQLFreeHandle failed\n");return;}conn = NULL;ret = SQLFreeHandle(SQL_HANDLE_ENV, env);if (!SQL_SUCCEEDED(ret)){printf("SQLFreeHandle failed\n");return;}env = NULL;
}int main(int argc, char **argv)
{test_SQLConnect();return 0;
}
通过以下命令进行编译,第一条命令用于将UnixODBC驱动库安装在系统路径下,第二条命令可用于源码编译安装时,将UnixODBC安装在自定义路径下,可找到对应的odbc动态库路径,通过-L指定库的路径,-I指定所需头文件的路径等等
gcc -o odbctest odbctest.c -lodbc -g -O0
gcc -o odbctest odbctest.c -L/usr/lib64/ -lodbc
执行可执行程序
[postgres@localhost:~/odbc]$ vim odbctest.c
[postgres@localhost:~/odbc]$
[postgres@localhost:~/odbc]$ gcc -o odbctest odbctest.c -lodbc -g -O0
[postgres@localhost:~/odbc]$
[postgres@localhost:~/odbc]$ ls
odbctest oracle-instantclient19.21-odbc-19.21.0.0.0-1.x86_64.rpm
odbctest.c oracle-instantclient19.21-sqlplus-19.21.0.0.0-1.x86_64.rpm
oracle-instantclient19.21-basic-19.21.0.0.0-1.x86_64.rpm oracle-instantclient19.21-tools-19.21.0.0.0-1.x86_64.rpm
oracle-instantclient19.21-devel-19.21.0.0.0-1.x86_64.rpm
[postgres@localhost:~/odbc]$
[postgres@localhost:~/odbc]$ ./odbctest
Connecting with SQLConnect...
connected success!
set autommit off success!
create table success!
insert table success!
SQLEndTran success!
SQLFreeHandle success!
SQLDisconnect success!
[postgres@localhost:~/odbc]$
编写 OCI 程序
oci应用程序的编写步骤和odbc流程一致,只是调用的接口不同,示例 如下:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>#include "oci.h" //引用OCI接口的头文件,因此编译时需要指定该头文件的路径/*user name and password*/
static text* username=(text *)"c##oracle";
static text* password=(text *)"123456";
static text* oracle=(text *)"192.168.1.14/orcl";/* Define SQL statements to be used in program. */
static text* SQL=(text *)"insert into t_oci(id1, id2) values (:1, :2)";/*handle define*/
static OCIEnv *p_env; //OCI environment handle
static OCIError *p_err; //OCI error handle
static OCISvcCtx *p_svc; //OCI service context handel
static OCIServer *p_ser; //OCI server handle
static OCISession *p_usr; //OCI user session handle
static OCIStmt *p_sql; //OCI statement handle
static OCIDefine *p_dfn = (OCIDefine *)NULL; //OCI define handle
static OCIBind *p_bnd = (OCIBind *)NULL; //OCI bind handle/*create OCI environment*/
int create_env()
{int swResult; //Return valueif(swResult = OCIEnvCreate(&p_env,OCI_DEFAULT,NULL,NULL,NULL,NULL,0,NULL)){printf("environment create error!\n\n");return -1;}else{printf("environment create success!\n\n");return 0;}
}/*init handle*/
int init_handle()
{int swResult;if(swResult = OCIHandleAlloc(p_env,(dvoid *)&p_ser,OCI_HTYPE_SERVER,0,NULL)) //服务器句柄{printf("init server handle error!\n\n");return -1;}else{printf("init server handle success!\n\n");}if(swResult = OCIHandleAlloc(p_env,(dvoid *)&p_err,OCI_HTYPE_ERROR,0,NULL)) //错误句柄{printf("init error handle error!\n\n");return -1;}else{printf("init error handle success!\n\n");}if(swResult = OCIHandleAlloc(p_env,(dvoid *)&p_usr,OCI_HTYPE_SESSION,0,NULL)) //事务句柄{printf("init session handle error!\n\n");return -1;}else{printf("init session handle success!\n\n");}if(swResult = OCIHandleAlloc(p_env,(dvoid *)&p_svc,OCI_HTYPE_SVCCTX,0,NULL)) //上下文句柄{printf("init service context handle error!\n\n");return -1;}else{printf("init service context handel success!\n\n");}if(swResult = OCIHandleAlloc(p_env,(dvoid *)&p_sql,OCI_HTYPE_STMT,0,NULL)) //SQL语句句柄{printf("init statement handle error!\n\n");return -1;}else{printf("init statement handle success!\n\n");}return 0;
}/*connect server*/
int conn_server()
{int swResult;if(swResult = OCILogon(p_env,p_err,&p_svc,(text *)username,strlen(username),(text *)password,strlen(password),(text *)oracle,strlen(oracle))){printf("connect error!\n\n");return -1;}elseprintf("connect success!\n\n");return 0;
}/*SQL statements*/
int oci_exec()
{int swResult;sb4 errcodep;ub4 recordno = 1;OraText bufp[1024];sword retcode = 0;//准备SQL语句if(swResult = OCIStmtPrepare(p_sql,p_err,SQL,strlen(SQL),OCI_NTV_SYNTAX,OCI_DEFAULT)){printf("prepare SQL statements error!\n\n");}else{printf("prepare SQL statements success!\n\n");}//设置绑定变量int getId1 ;int getId2;//char getName[10];OCIBind *p_bndp1 = NULL;OCIBind *p_bndp2 = NULL;printf("输入ID1,ID2:\n");scanf("%d %d",&getId1,&getId2);if(swResult = OCIBindByPos(p_sql,&p_bndp1,p_err,1,(dvoid *)&getId1,(sb4)sizeof(getId1),SQLT_INT,NULL,NULL,NULL,0,NULL,OCI_DEFAULT)){printf("Bind p1 error!\n\n");return -1;}else{printf("bind success!\n\n");}if(swResult = OCIBindByPos(p_sql,&p_bndp2,p_err,2,(dvoid *)&getId2,(sb4)sizeof(getId2),SQLT_INT,NULL,NULL,NULL,0,NULL,OCI_DEFAULT)){printf("Bind p1 error!\n\n");return -1;}else{printf("bind success!\n\n");}/*if(swResult = OCIBindByPos(p_sql,&p_bndp2,p_err,2,&getName,(sb4)sizeof(getName),SQLT_STR, (dvoid *) 0, (ub2 *) 0, (ub2 *) 0, (ub4) 0, (ub4 *) 0, OCI_DEFAULT)){printf("Bind p2 error!\n\n");return -1;}else{printf("bind success!\n\n");}*///执行SQL statementsif(swResult = OCIStmtExecute(p_svc,p_sql,p_err,1,0,NULL,NULL,OCI_DEFAULT)){//printf("execute SQL statement error!\n\n");if (OCIErrorGet(p_err, recordno++, NULL, &errcodep, (OraText *)bufp, sizeof(bufp), OCI_HTYPE_ERROR) == OCI_SUCCESS)printf("error msg:%s\n", bufp);return -1;}else{printf("execute SQL statement success!\n\n");}return 0;
}/*quit server*/
void quit_server()
{OCILogoff(p_svc,p_err);printf("Quit success!\n");
}/*free handle*/
void free_handle()
{OCIHandleFree(p_ser,OCI_HTYPE_SERVER); //释放服务器句柄OCIHandleFree(p_err,OCI_HTYPE_ERROR); //释放错误句柄OCIHandleFree(p_usr,OCI_HTYPE_SESSION); //释放事务句柄OCIHandleFree(p_svc,OCI_HTYPE_SVCCTX); //释放上下文句柄OCIHandleFree(p_sql,OCI_HTYPE_STMT); //释放SQL语句句柄
}int main()
{if(create_env() == -1) //创建环境return -1;if(init_handle() == -1) //初始化句柄return -1;if(conn_server() == -1) //连接数据库return -1;if(oci_exec() == -1)return -1;/*quit_server(); //退出数据库free_handle(); //释放句柄 */return 0;
}
如上示例中可以看到调用OCI相关接口需要引用oci.h
这个头文件,那么 这个头文件在哪?该头文件是在安装完oracle
客户端之后,对应oracle-instantclient19.21-devel-19.21.0.0.0-1.x86_64.rpm
软件包,可以通过如下命令查看其安装之后的路径,如下:
[postgres@localhost:~/odbc]$ rpm -qpl oracle-instantclient19.21-devel-19.21.0.0.0-1.x86_64.rpm
/usr/include/oracle
/usr/include/oracle/19.21
/usr/include/oracle/19.21/client64
/usr/include/oracle/19.21/client64/ldap.h
/usr/include/oracle/19.21/client64/nzerror.h
/usr/include/oracle/19.21/client64/nzt.h
/usr/include/oracle/19.21/client64/occi.h
/usr/include/oracle/19.21/client64/occiAQ.h
/usr/include/oracle/19.21/client64/occiCommon.h
/usr/include/oracle/19.21/client64/occiControl.h
/usr/include/oracle/19.21/client64/occiData.h
/usr/include/oracle/19.21/client64/occiObjects.h
/usr/include/oracle/19.21/client64/oci.h
/usr/include/oracle/19.21/client64/oci1.h
/usr/include/oracle/19.21/client64/oci8dp.h
/usr/include/oracle/19.21/client64/ociap.h
/usr/include/oracle/19.21/client64/ociapr.h
/usr/include/oracle/19.21/client64/ocidef.h
/usr/include/oracle/19.21/client64/ocidem.h
/usr/include/oracle/19.21/client64/ocidfn.h
/usr/include/oracle/19.21/client64/ociextp.h
/usr/include/oracle/19.21/client64/ocikpr.h
/usr/include/oracle/19.21/client64/ociver.h
/usr/include/oracle/19.21/client64/ocixmldb.h
/usr/include/oracle/19.21/client64/ocixstream.h
/usr/include/oracle/19.21/client64/odci.h
/usr/include/oracle/19.21/client64/oratypes.h
/usr/include/oracle/19.21/client64/ori.h
/usr/include/oracle/19.21/client64/orid.h
/usr/include/oracle/19.21/client64/orl.h
/usr/include/oracle/19.21/client64/oro.h
/usr/include/oracle/19.21/client64/ort.h
/usr/include/oracle/19.21/client64/xa.h
/usr/lib/oracle
/usr/lib/oracle/19.21
/usr/lib/oracle/19.21/client64
/usr/lib/oracle/19.21/client64/lib
/usr/lib/oracle/19.21/client64/lib/ottclasses.zip
/usr/share/oracle
/usr/share/oracle/19.21
/usr/share/oracle/19.21/client64
/usr/share/oracle/19.21/client64/admin
/usr/share/oracle/19.21/client64/admin/oraaccess.xsd
/usr/share/oracle/19.21/client64/demo
/usr/share/oracle/19.21/client64/demo/cdemo81.c
/usr/share/oracle/19.21/client64/demo/demo.mk
/usr/share/oracle/19.21/client64/demo/occidemo.sql
/usr/share/oracle/19.21/client64/demo/occidemod.sql
/usr/share/oracle/19.21/client64/demo/occidml.cpp
/usr/share/oracle/19.21/client64/demo/occiobj.cpp
/usr/share/oracle/19.21/client64/demo/occiobj.typ
/usr/share/oracle/19.21/client64/demo/oraaccess.xml
/usr/share/oracle/19.21/client64/demo/ott
/usr/share/oracle/19.21/client64/demo/setuporamysql.sh
/usr/share/oracle/19.21/client64/doc
/usr/share/oracle/19.21/client64/doc/SDK_LICENSE
/usr/share/oracle/19.21/client64/doc/SDK_README
[postgres@localhost:~/odbc]$
接下来编译上述样例:
[postgres@localhost:~/odbc]$ echo $ORACLE_HOME
/usr/lib/oracle/19.21/client64
[postgres@localhost:~/odbc]$
[postgres@localhost:~/odbc]$ gcc -o ocitest ocitest.c -I/usr/include/oracle/19.21/client64 -L${ORACLE_HOME}/lib -lclntsh -g -O0
[postgres@localhost:~/odbc]$
执行可执行程序,如下:
[postgres@localhost:~/odbc]$ sqlplus c##oracle/123456@192.168.1.14:1521/orclSQL*Plus: Release 19.0.0.0.0 - Production on Tue Dec 19 01:02:19 2023
Version 19.21.0.0.0Copyright (c) 1982, 2022, Oracle. All rights reserved.Last Successful login time: Tue Dec 19 2023 01:00:31 -08:00Connected to:
Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production
Version 19.3.0.0.0SQL> create table t_oci(id1 int, id2 int);Table created.SQL> Disconnected from Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production
Version 19.3.0.0.0
[postgres@localhost:~/odbc]$
[postgres@localhost:~/odbc]$ ./ocitest
environment create success!init server handle success!init error handle success!init session handle success!init service context handel success!init statement handle success!connect success!prepare SQL statements success!输入ID1,ID2:
1 2
bind success!bind success!execute SQL statement success![postgres@localhost:~/odbc]$