DM ODBC 编程指南

本章结合DM数据库的特点,比较全面系统地介绍ODBC的基本概念以及DM ODBCDRIVER的使用方法,以便用户更好地使用DM ODBC编写应用程序。

ODBC提供访问不同类型的数据库的途径。结构化查询语言SQL是一种用来访问数据库的语言。通过使用ODBC,应用程序能够使用相同的源代码和各种各样的数据库交互。这使得开发者不需要以特殊的数据库管理系统DBMS为目标,或者了解不同支撑背景的数据库的详细细节,就能够开发和发布客户/服务器应用程序。

DM ODBC 3.0遵照Microsoft ODBC3.0规范设计与开发,实现了ODBC应用程序与DM的互连接口。用户可以直接调用DM ODBC 3.0接口函数访问DM,也可以使用可视化编程工具如C++ Builder、PowerBuilder等利用DM ODBC 3.0访问DM。

在DM客户端软件安装过程中,如果选择了安装ODBC驱动程序的相关选项,安装工具可完成将DM ODBC 3.0驱动程序复制到硬盘,并在Windows注册表中登记DMODBC驱动程序信息的工作。若使用的是拷贝版,在Windows系统上手动注册odbc驱动的方法为:创建注册文件(例如installDmOdbc.reg),文件内容为:

REGEDIT4
[HKEY_LOCAL_MACHINE\Software\ODBC\ODBCINST.INI\ODBC Drivers]
"DM8 ODBC DRIVER"="Installed"
[HKEY_LOCAL_MACHINE\Software\ODBC\ODBCINST.INI\DM8 ODBC DRIVER]
"Driver"="%DM_HOME%\\bin\\dodbc.dll"  //此处修改成你的dodbc.dll所在目录
"Setup"="%DM_HOME%\\bin\\dodbc.dll"   //此处修改成你的dodbc.dll所在目录

要进一步使用DM ODBC驱动程序,请阅读本章以了解ODBC数据源管理方法。

3.1 数据类型

客户程序可以通过SQLGetTypeInfo函数来获取DM ODBC 3.0支持的数据类型信息。由SQLGetTypeInfo返回的数据类型是数据源所支持的数据类型,它们是预备用于DDL(DataDefinitionLanguage)语句的。

调用DM ODBC 3.0的SQLGetTypeInfo,返回支持的数据类型如表3.1所示:

表3.1 数据类型列表
类型名 类型描述
Char(n) 固定串长度为n的字符串,n<=8188
Varchar(n) 最大字符串长度为n的可变长度字符串,n<=8188
Binary(n) 固定长度为n的二进制数据,n<=8188
Varbinary(n) 最大长度为n的可变长度二进制数据,n<=8188
Image 影像数据类型,可变长度的二进制数据,最大长度为2G-1
Text 文本数据类型,可变长度的字符数据,最大长度为2G-1
Bit 单个二进制位数据
Tinyint 精度为3,刻度为0的有符号精确数字,取值范围-128…127
Smallint 精度为5,刻度为0的有符号精确数字,取值范围-32,768…32,767
Int 精度为10,刻度为0的有符号精确数字,取值范围-2[31]…2[31]-1
Bigint 精度为19,刻度为0的有符号精确数字值,取值范围-2[63]…2[63]-1
Real 二进制精度为24的有符号近似数字值,取值范围0或者绝对值为: 10[-38]…10[38]
Float 二进制精度为53的有符号近似数字值,取值范围0或者绝对值为:
Double 二进制精度为53的有符号近似数字值,取值范围0或者绝对值为:
Decimal(p,s) 精度为p,刻度为s的有符号精确数字值,1≤p≤38,s≤p
Numeric(p,s) 精度为p,刻度为s的有符号精确数字值,1≤p≤38,s≤p
Date 日期数据类型,年月日字段,格式与Gregorian(罗马)日历一致
Time(p) 时间数据类型,时分秒字段,精度p指定了秒的精度
Timestamp(p) 时间戳数据类型,年月日时分秒字段,精度p指定了秒的精度
Interval year(p) 年间隔,即两个日期之间的年数字,p为时间间隔的首项字段精度(后面简称为:首精度)
Interval month(p) 月间隔,即两个日期之间的月数字,p为时间间隔的首精度
Interval year(p) to month 年月间隔,即两个日期之间的年月数字,p为时间间隔的首精度
Interval day(p) 日间隔,即为两个日期/时间之间的日数字,p为时间间隔的首精度
Interval hour(p) 时间隔,即为两个日期/时间之间的时数字,p为时间间隔的首精度
Interval minute(p) 分间隔,即为两个日期/时间之间的分数字,p为时间间隔的首精度
Interval second(p,q) 秒间隔,即为两个日期/时间之间的秒数字,p为时间间隔的首精度,q为时间间隔秒精度
Interval day(p) to hour 日时间隔,即为两个日期/时间之间的日时数字,p为时间间隔的首精度
Interval day(p) to minute 日时分间隔,即为两个日期/时间之间的日时分数字,p为时间间隔的首精度
Interval day(p) to second(q) 日时分秒间隔,即为两个日期/时间之间的日时分秒数字,p为时间间隔的首精度,q为时间间隔秒精度
Interval hour(p) to minute 时分间隔,即为两个日期/时间之间的时分数字,p为时间间隔的首精度
Interval hour(p) to second(q) 时分秒间隔,即为两个日期/时间之间的时分秒数字,p为时间间隔的首精度,q为时间间隔秒精度
Interval minute(p) to second(q) 分秒间隔,即为两个日期/时间之间的分秒间隔,p为时间间隔的首精度,q为时间间隔秒精度

注:变长字符串的最大长度受页大小的约束,最大长度为页大小的一半且不超过8188个字节。

3.2 支持的函数

客户程序可以通过SQLGetFunctions函数来获取DM ODBC 3.0支持的函数信息。由SQLGetFunctions返回的函数列表是数据源所支持的函数。

以下按照类型分类列出了DM ODBC 3.0提供的函数。应用程序能够通过调用SQLGetFunctions来获得指定函数的支持信息。

3.2.1 连接到数据源

下面的函数用于连接到数据源:

  1. SQLAllocHandle:分配环境、连接、语句或者描述符句柄;
  2. SQLConnect:建立与驱动程序或者数据源的连接。访问数据源的连接句柄包含了状态、事务申明和错误信息的所有连接信息;
  3. SQLDriverConnect:与SQLConnect相似,用来连接到驱动程序或者数据源。但它比SQLConnect支持数据源更多的连接信息,它提供了一个对话框来提示用户设置所有的连接信息以及系统信息表没有定义的数据源;
  4. SQLBrowseConnect:支持一种交互方法来检索或者列出连接数据源所需要的属性和属性值。每次调用函数可以获取一个连接属性字符串,当检索完所有的属性值,就建立起与数据源的连接,并且返回完整的连接字符串,否则提示缺少的连接属性信息,用户根据此信息重新输入连接属性值再次调用此函数进行连接。

达梦支持的连接关键字及取值范围:

名称 说明
DRIVER DM ODBC驱动的名字:DM8 ODBC DRIVER
DSN 数据源名称
SERVER 目标服务器:ip地址、或者服务名、或unixsocket文件路径名。其中,unixsocket路径文件名必须和PROTOCOL_TYPE为UNIXSOCKET搭配使用
TCP_PORT 端口号
PROTOCOL_TYPE 协议类型:UNIXSOCKET等:TCP、UDP、IPC(共享内存)、RDMA(远程直接内存访问)或UNIXSOCKET。当PROTOCOL_TYPE为UNIXSOCKET时,SERVER必须为unixsocket文件路径名。只有Linux环境才支持UNIXSOCKET,Windows不支持(设置了也会被忽略)
UID 用户名
PWD 密码
LANGUAGE 使用的语言信息:ENGLISH,CHINESE
SSL_PATH 加密文件路径
SSL_PWD 加密文件密码
MPP_LOGIN MPP登陆方式:MPP_GLOBAL,MPP_LOCAL
CHARACTER_CODE 编码信息:PG_UTF8/PG_GB18030,PG_BIG5,PG_ISO_8859_9,PG_EUC_JP,PG_EUC_KR,PG_KOI8R,PG_ISO_8859_1,PG_ISO_8859_11
FAST_INSERT 是否配置快速插入:TRUE,FALSE
RW_SEPARATE 是否配置读写分离:TRUE,FALSE
RW_SEPARATE_PERCENT 读写分离的比例:0~100
UDP_FLAG 是否使用UDP通信:TRUE,FALSE
TCNAME_LOWER 是否将通过描述获取的表名和列名转换为小写:TRUE,FALSE

3.2.2 获取驱动程序和数据源信息

下面的函数用来获取驱动程序和数据源信息:

  1. SQLDataSources:能够被调用多次来获取应用程序使用的所有数据源的名字;
  2. SQLDrivers:返回所有安装过的驱动程序清单,包括对它们的描述以及属性关键字;
  3. SQLGetInfo:返回连接的驱动程序和数据源的元信息;
  4. SQLGetFunctions:返回指定的驱动程序是否支持某个特定函数的信息;
  5. SQLGetTypeInfo:返回指定的数据源支持的数据类型的信息。

3.2.3 设置或者获取驱动程序属性

下面的函数用来设置或者获取驱动程序属性:

  1. SQLSetConnectAttr:设置连接属性值;
  2. SQLGetConnectAttr:返回连接属性值;
  3. SQLSetEnvAttr:设置环境属性值;
  4. SQLGetEnvAttr:返回环境属性值;
  5. SQLSetStmtAttr:设置语句属性值;
  6. SQLGetStmtAttr:返回语句属性值。

3.2.4 设置或者获取描述符字段

下面的函数用来设置或者获取描述符字段:

  1. SQLGetDescField:返回单个描述符字段的值;
  2. SQLGetDescRec:返回当前描述符记录的多个字段的值;
  3. SQLSetDescField:设置单个描述符字段的值;
  4. SQLSetDescRec:设置描述符记录的多个字段。

3.2.5 准备SQL语句

下面的函数用来准备SQL语句:

  1. SQLPrepare:准备要执行的SQL语句;
  2. SQLBindParameter:在SQL语句中分配参数的缓冲区;
  3. SQLGetCursorName:返回与语句句柄相关的游标名称;
  4. SQLSetCursorName:设置与语句句柄相关的游标名称;
  5. SQLSetScrollOptions:设置控制游标行为的选项。

3.2.6 提交SQL请求

下面的函数用来提交SQL请求:

  1. SQLExecute:执行准备好的SQL语句;
  2. SQLExecDirect:执行一条SQL语句;
  3. SQLNativeSql:返回驱动程序对一条SQL语句的翻译;
  4. SQLDescribeParam:返回对SQL语句中指定参数的描述;
  5. SQLNumParams:返回SQL语句中参数的个数;
  6. SQLParamData:与SQLPutData联合使用在运行时给参数赋值;
  7. SQLPutData:在SQL语句运行时给部分或者全部参数赋值。

3.2.7 检索结果集及其相关信息

下面的函数用来检索结果集及其相关信息:

  1. SQLRowCount:返回INSERT、UPDATE或者DELETE等语句影响的行数;
  2. SQLNumResultCols:返回结果集中列的数目;
  3. SQLDescribeCol:返回结果集中列的描述符记录;
  4. SQLColAttribute:返回结果集中列的属性;
  5. SQLBindCol:为结果集中的列分配缓冲区;
  6. SQLFetch:在结果集中检索下一行元组;
  7. SQLFetchScroll:返回指定的结果行;
  8. SQLGetData:返回结果集中当前行某一列的值;
  9. SQLSetPos:在取到的数据集中设置游标的位置。这个记录集中的数据能够刷新、更新或者删除;
  10. SQLBulkOperations:执行块插入和块书签操作,其中包括根据书签更新、删除或者取数据;
  11. SQLMoreResults:确定是否能够获得更多的结果集,如果能就执行下一个结果集的初始化操作;
  12. SQLGetDiagField:返回一个字段值或者一个诊断数据记录;
  13. SQLGetDiagRec:返回多个字段值或者一个诊断数据记录。

3.2.8 取得数据源系统表的信息

下面的函数用来取得数据源系统表的信息:

  1. SQLColumnPrivileges:返回一个关于指定表的列的列表以及相关的权限信息;
  2. SQLColumns:返回指定表的列信息的列表;
  3. SQLForeignKeys:返回指定表的外键信息的列表;
  4. SQLPrimaryKeys:返回指定表的主键信息的列表;
  5. SQLProcedureColumns:返回指定存储过程的参数信息的列表;
  6. SQLProcedures:返回指定数据源的存储过程信息的列表;
  7. SQLSpecialColumns:返回唯一确定某一行的列的信息,或者当某一事务修改一行的时候自动更新各列的信息;
  8. SQLStatistics:返回一个单表的相关统计信息和索引信息;
  9. SQLTablePrivileges:返回相关各表的名称以及相关的权限信息;
  10. SQLTables:返回指定数据源中表信息。

3.2.9 终止语句执行

下面的函数用来终止语句执行:

  1. SQLFreeStmt:终止语句执行,关闭所有相关的游标,放弃没有提交的结果,选择释放与指定语句句柄相关的资源;
  2. SQLCloseCursor:关闭一个打开的游标,放弃没有提交的结果;
  3. SQLCancel:放弃执行一条SQL语句;
  4. SQLEndTran:提交或者回滚事务。

3.2.10 中断连接

下面的函数处理中断连接的任务:

  1. SQLDisconnect:关闭指定连接;
  2. SQLFreeHandle:释放环境、连接、语句或者描述符句柄。

3.3 建立ODBC连接

3.3.1 申请环境与连接句柄

客户程序要和一个远程的服务器或数据库进行通讯,必须首先和这个服务器或数据库建立连接。下面我们将要介绍如何通过ODBC建立一个连接以及使用连接。

为了建立一个ODBC数据源连接,需要使用到环境句柄以及连接句柄。句柄有一个层次的概念,一个连接句柄总是和一个唯一的环境句柄相联系的,所有的连接句柄必须在环境句柄释放之前释放。

客户程序可以通过调用函数SQLAllocHandle来申请一个环境句柄,调用函数SQLAllocHandle时必须传入句柄选项SQL_HANDLE_ENV,当申请环境句柄成功之后,可以在此环境句柄上申请连接句柄。申请环境句柄和连接句柄的代码示范如下:

##include <windows.h>
##include <sql.h>
##include <sqltypes.h>
##include <sqlext.h>
/* 检测返回代码是否为成功标志,当为成功标志时返回TRUE,否则返回FALSE */
##define RC_SUCCESSFUL(rc)	((rc) == SQL_SUCCESS || (rc) == SQL_SUCCESS_WITH_INFO)
/* 检测返回代码是否为失败标志,当为失败标志时返回TRUE,否则返回FALSE */
##define RC_NOTSUCCESSFUL(rc) (!(RC_SUCCESSFUL(rc)))
HENV		henv;	/* 环境句柄 */
HDBC		hdbc;	/* 连接句柄 */
SQLRETURN	sret;	/* 返回代码 */
void main(void)
{
/* 申请一个环境句柄       */
SQLAllocHandle(SQL_HANDLE_ENV, NULL, &henv);
/* 设置环境句柄的ODBC版本 */
SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3, 
SQL_IS_INTEGER);
/* 申请一个连接句柄       */
SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc);
/* 释放连接句柄           */
SQLFreeHandle(SQL_HANDLE_DBC, hdbc);
/* 释放环境句柄           */
SQLFreeHandle(SQL_HANDLE_ENV, henv);
}

3.3.2 如何与数据源进行连接

ODBC的连接是从数据源开始的,数据源是ODBC对一个特定的数据库的别称。为了访问由数据源提供的数据,你的程序中必须首先建立和数据源之间的连接,在环境和连接句柄正确分配之后,才能通过这些连接管理数据访问。

为了产生建立连接时必要的参数,必须完成以下的几项工作:

  1. 调用SQLAllocHandle申请一个环境句柄;
  2. 调用SQLAllocHandle申请一个连接句柄;
  3. 创建一个数据源DSN;
  4. 一个有效的用户ID;
  5. 一个对应于这个用户ID的口令;
  6. 其它的一些提供给驱动程序的参数信息。

连接ODBC数据源时,ODBC提供三种不同的连接函数,即SQLConnect、SQLDriverConnect和SQLBrowseConnect,每个函数都有不同的参数以及不同级别的一致性,如表3.2所示:

表3.2 连接到数据源的ODBC函数
函数 ODBC版本 一致性 主要参数
SQLConnect 1.0 核心级 hdbc,数据源,用户ID,口令
SQLDriverConnect 1.0 1级 hdbc,窗口句柄,输入连接字符串
SQLBrowseConnect 1.0 2级 hdbc,输入连接字符串,输出连接字符串

SQLConnect是连接ODBC数据源的最基本的方法,在所有的一致性级别上都支持。SQLConnect函数有以下参数:连接句柄、数据源名称、数据源名称长度、用户名(用户ID)、用户名长度、用户口令以及口令长度。返回代码有:SQL_SUCCESS,SQL_SUCCESS_WITH_INFO,SQL_ERROR或者SQL_INVALID_HANDLE。使用SQLConnect函数连接数据源的代码示范如下:

sret = SQLConnect(hdbc, (SQLCHAR *)"DM", SQL_NTS, (SQLCHAR *)"SYSDBA", SQL_NTS, (SQLCHAR *)"SYSDBA", SQL_NTS);
if (RC_NOTSUCCESSFUL(sret)) {
/* 连接数据源失败! */
…进行相应的错误处理…
exit(0);
}

SQLDriverConnect提供了比SQLConnect更灵活的方法来建立ODBC连接。它支持以下几种连接:要求更多连接参数的数据源,对话框提示用户输入所有的连接信息以及没有在系统信息表中定义的数据源。

SQLDriverConnect提供以下的连接方法:

  1. 用一个连接字符串建立一个连接,这个字符串包括建立连接的所有数据,如DSN,一个或多个用户ID及其口令,以及其他的数据库所需要的连接信息;
  2. 用一个并不完整的连接字符串来建立连接,使得ODBC驱动程序管理器来提示用户输入所需要的连接信息;
  3. 用一个没有在系统信息表中登记的数据源建立连接,驱动程序自动提示用户输入连接信息;
  4. 用一个连接字符串建立连接,这个字符串在DSN配置文件中是确定的。

SQLDriverConnect函数fDriverCompletion参数说明:

  1. SQL_DRIVER_PROMPT:设置此选项用来显示一个对话框来提示用户输入连接信息;
  2. SQL_DRIVER_COMPLETE:如果函数调用中包含了足够的信息,ODBC就进行连接,否则弹出对话框提示用户输入连接信息,此时等同于SQL_DRIVER_PROMPT;
  3. SQL_DRIVER_COMPLETE_REQUIRED:这个参数与SQL_DRIVER_COMPLETE参数唯一的不同是用户不能改变由函数提供的信息;
  4. SQL_DRIVER_NOPROMPT:如果函数调用时有足够的信息,ODBC就进行连接,否则返回SQL_ERROR。

使用SQLDriverConnect连接数据源的代码示范如下:

SQLCHAR szConnStrIn[256] = "DSN=DM;DRIVER=DM ODBC DRIVER;
UID=SYSDBA;PWD=SYSDBA;TCP_PORT=5236";
SQLCHAR szConnStrOut[256];
SQLSMALLINT cbConnStrOut;
	sret = SQLDriverConnect(hdbc, NULL, szConnStrIn, SQL_NTS, szConnStrOut, 256, &cbConnStrOut, SQL_DRIVER_NOPROMPT);
if (RC_NOTSUCCESSFUL(sret)) {
/* 连接数据源失败! */
…进行相应的错误处理…
exit(0);
}

SQLBrowseConnect函数与SQLDriverConnect函数相似,但是调用SQLBrowseConnect函数时,程序在运行时可以再形成一个连接字符串,使用这个函数可以用一个交互的方式来决定连接到数据源时所需要的一些信息。

使用SQLBrowseConnect函数连接数据源的代码示范如下:

SQLCHAR szConnStrIn[256] = "";
SQLCHAR szConnStrOut[256];
SQLSMALLINT cbConnStrOut;
strcpy(szConnStrIn, "DRIVER=DM ODBC DRIVER");
sret = SQLBrowseConnect(hdbc, szConnStrIn, SQL_NTS, szConnStrOut, 256, &cbConnStrOut);
if (sret != SQL_NEED_DATA) {
/* 连接数据源失败! */
…进行相应的错误处理…
exit(0);
}
strcpy(szConnStrIn, "SERVER=127.0.0.1;TCP_PORT=5236");//TCP_PORT=5236可选
sret = SQLBrowseConnect(hdbc, szConnStrIn, SQL_NTS, szConnStrOut, 256, &cbConnStrOut);
if (sret != SQL_NEED_DATA) {
/* 连接数据源失败! */
…进行相应的错误处理…
exit(0);
}
strcpy(szConnStrIn, "UID=SYSDBA;PWD=SYSDBA;");
sret = SQLBrowseConnect(hdbc, szConnStrIn, SQL_NTS, szConnStrOut, 256, &cbConnStrOut);
if (sret != SQL_SUCCESS) {
/* 连接数据源失败! */
…进行相应的错误处理…
exit(0);
}
/*连接成功*/

3.3.3 设置与取得连接的属性

建立连接之后,应用程序可以通过调用SQLSetConnectAttr函数来设置连接属性,对连接进行全方面的管理。表3.3列出了一些常用的连接属性。

表3.3 常用的连接属性
属性 描述
SQL_ATTR_ACCESS_MODE 用来设置访问模式,即只读或者读写连接模式, 可以用来优化并发控制策略。
SQL_ATTR_ASYNC_ENABLE 是否支持异步执行。
SQL_ATTR_AUTOCOMMIT 是否使用自动提交功能。
SQL_ATTR_CONNECTION_TIMEOUT 设定连接上的超时。
SQL_ATTR_CURRENT_CATALOG 当前连接使用的编目。
SQL_ATTR_LOGIN_TIMEOUT 设定登录超时。
SQL_ATTR_ODBC_CURSORS 设置驱动程序管理器使用游标的方式。
SQL_ATTR_PACKET_SIZE 设置网络传输包的大小。暂不支持。
SQL_ATTR_QUIET_MODE 使弹出对话框有效/无效。

更多的连接属性,用户可以参考《Microsoft ODBC 3.0程序员参考手册》,在这里不做详细介绍。

应用程序可以通过调用SQLGetConnectAttr函数来取得当前连接的属性。

设置与取得连接属性的代码示范如下:

SQLINTEGER	AUTOCOMMIT_MODE;
/* 设置连接句柄属性,关闭自动提交功能 */
SQLSetConnectAttr(hdbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)SQL_AUTOCOMMIT_OFF,
SQL_IS_INTEGER);
/* 取得连接句柄属性,取得提交的模式   */
SQLGetConnectAttr(hdbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)&AUTOCOMMIT_MODE,
sizeof(SQLINTEGER), NULL); 

3.3.4 断开与数据源之间的连接

如果要终止客户程序与服务器之间的连接,客户程序应当完成以下的几个操作:

  1. 调用SQLFreeHandle释放语句句柄,关闭所有打开的游标,释放相关的语句句柄资源。(在非自动提交模式下,需事先提交当前的事务);
  2. 调用函数SQLDisconnect关闭所有的连接;
  3. 调用SQLFreeHandle释放连接句柄及其相关的资源;
  4. 调用SQLFreeHandle释放环境句柄及其相关的资源;

一个完整的连接管理示范代码如下:

##include <windows.h>
##include <sql.h>
##include <sqltypes.h>
##include <sqlext.h>
/* 检测返回代码是否为成功标志,当为成功标志返回TRUE,否则返回FALSE */
##define RC_SUCCESSFUL(rc) ((rc) == SQL_SUCCESS || (rc) == SQL_SUCCESS_WITH_INFO)
/* 检测返回代码是否为失败标志,当为失败标志返回TRUE,否则返回FALSE */
##define RC_NOTSUCCESSFUL(rc) (!(RC_SUCCESSFUL(rc)))
HENV			henv;	/* 环境句柄 */
HDBC			hdbc;	/* 连接句柄 */
HSTMT			hsmt;	/* 语句句柄 */
SQLRETURN		sret;	/* 返回代码 */
SQLINTEGER	AUTOCOMMIT_MODE;
void main(void)
{
/* 申请一个环境句柄      */
SQLAllocHandle(SQL_HANDLE_ENV, NULL, &henv);
/* 设置环境句柄的ODBC版本 */
SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3,
 SQL_IS_INTEGER);
/* 申请一个连接句柄       */
SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc);
sret = SQLConnect(hdbc, (SQLCHAR *)"DM", SQL_NTS, (SQLCHAR *)"SYSDBA", SQL_NTS, (SQLCHAR *)"SYSDBA", SQL_NTS);
if (RC_NOTSUCCESSFUL(sret)) {
/* 连接数据源失败! */
SQLFreeHandle(SQL_HANDLE_DBC, hdbc);
SQLFreeHandle(SQL_HANDLE_ENV, henv);
exit(0);
}
/* 设置连接句柄属性,关闭自动提交功能 */
SQLSetConnectAttr(hdbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)SQL_AUTOCOMMIT_OFF,
SQL_IS_INTEGER);
/* 取得连接句柄属性,取得提交的模式   */
SQLGetConnectAttr(hdbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)&AUTOCOMMIT_MODE, 
sizeof(SQLINTEGER), NULL);
/* 申请一个语句句柄                   */
SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hsmt);
…在这里可以使用语句句柄进行相应的数据库操作…
/* 释放语句句柄                       */
SQLFreeHandle(SQL_HANDLE_STMT, hsmt);
/* 提交连接上的事务                   */
SQLEndTran(SQL_HANDLE_DBC, hdbc, SQL_COMMIT);
/* 断开与数据源之间的连接             */
SQLDisconnect(hdbc);
/* 释放连接句柄           */
SQLFreeHandle(SQL_HANDLE_DBC, hdbc);
/* 释放环境句柄           */
SQLFreeHandle(SQL_HANDLE_ENV, henv);
}

3.4 ODBC应用程序编程的基本步骤

3.4.1 Windows上创建ODBC数据源

在客户使用ODBC方法访问一个DM数据库服务器之前,必须先对自己的应用程序所用的ODBC数据源进行配置。本节将介绍如何为你的应用程序安装和配置ODBC数据源。

在客户机上配置ODBC数据源的步骤:

  1. 在控制面板\管理工具中访问ODBC构件,显示ODBC数据源管理器对话框,如图3.1所示:ODBC数据源管理器对话框包含的标签如下:

    1. 用户DSN:添加、删除或配置本机上的数据源,它们只可由当前用户使用;
    2. 系统DSN:添加、删除或配置本机上的数据源,它们可由任何用户使用;
    3. 文件DSN:添加、删除或配置在分离文件中的数据源。这些文件可以被安装了同样数据库驱动器的用户共享;
    4. 驱动程序:列出了安装在客户机上的数据库驱动器;
    5. 跟踪:用于测试你的数据库应用程序。它跟踪客户机和数据库服务器之间的ODBC API的调用;
    6. 连接池:允许不同的应用程序自动复用多个连接。这有助于限制和数据库服务器的通信过载;
    7. 关于:显示主要ODBC组件的版本。

ODBC数据源管理器对话框

图3.1 ODBC数据源管理器对话框
  1. 设置和配置一个系统DSN,请单击系统DSN标签,单击添加按钮增加一个新的DSN,显示如图3.2所示的对话框。

创建新数据源对话框

图3.2 创建新数据源对话框
  1. 选择DM ODBC 3.0驱动程序即DM ODBC DRIVER,单击完成按钮,显示如图3.3所示的DM ODBC 3.0数据源配置对话框。

创建新的DM数据源对话框

图3.3 创建新的DM数据源对话框
  1. 输入数据源的名称、一个简单的描述,并选择你想要连接的数据库服务器的名字、使用的端口号、验证登录用户ID真伪的方式,如果使用DMServer验证方式则需要输入登录数据源的ID以及密码等信息,选择系统提示信息的语种,以及选择是否使用DMServer的增强选项。

    如果连接属性填写的是UNIXSOCKET,那么服务器需填写LINUX环境下的unixsocket文件路径名。两者需配套使用。只有Linux环境才支持UNIXSOCKET,Windows不支持(设置了也会被忽略)。

  2. 单击测试按钮测试你配置的数据源是否正确,得到数据源测试报告如图3.4所示,如果测试成功,可以单击确定按钮以保存你设置的新的系统数据源,如图3.5所示:

数据源测试报告

图3.4 数据源测试报告

完成系统数据源的设置

图3.5 完成系统数据源的设置
  1. 单击确定按钮关闭ODBC数据源管理器对话框。

3.4.2 Linux上创建ODBC数据源

DMODBC在Linux操作系统上的使用依赖于UnixODBC库,如果UnixODBC未安装在系统目录下,则为了能使DMODBC能找到需要的库文件,用户需要设置系统环境变量LD_LIBRARY_PATH指向动态库。另外,如果安装的UnixODBC生成的动态库名称不是libodbcinst.so(如libodbcinst.so.1.0.0或者libodbcinst.so.2.0.0等),则需要对实际库文件建立符号链接。

在Linux上配置ODBC数据源采用命令行修改的方式。本节将介绍如何为你的应用程序配置ODBC数据源。

  1. 编辑/etc/odbcinst.ini,输入如下内容:
[DM8 ODBC DRIVER]
Description 	= ODBC DRIVER FOR DM8
Driver 			= /lib/libdodbc.so
  1. 编辑/etc/odbc.ini,输入如下内容:

使用TCP通信协议(PROTOCOL_TYPE缺省情况下,为使用TCP方式)。

[dm]
Description 	= DM ODBC DSN
Driver 			= DM8 ODBC DRIVER
SERVER 			= localhost
UID 			= SYSDBA
PWD 			= SYSDBA
TCP_PORT		= 5236

使用UINXSOCKET网络连接协议。

[dm] 
Description    = DM ODBC DSN
Driver         = DM8 ODBC DRIVER
SERVER         = /data/sdb/DAMENG/foo.sock               //unixsocket文件路径名
UID            = SYSDBA 
PWD            = SYSDBA 
TCP_PORT       = 5236
LINK_ATTP = PROTOCOL_TYPE = UNIXSOCKET                   //UNIXSOCKET类型

注意事项:

  1. odbc.ini中的Driver内容一定要与odbcinst.ini中的达梦驱动定义的节点名称相同。
  2. odbc.ini中的SERVER可以输入数据库服务器的IP,或者unixsocket文件路径名。

3.4.3 ODBC应用程序编写的基本步骤

应用程序使用ODBC访问数据源,可以按照以下几个基本步骤进行:

  1. 调用函数SQLAllocHandle申请环境、连接句柄,调用函数SQLSetEnvAttr设置环境句柄属性,调用函数SQLSetConnectAttr设置连接句柄属性,调用连接函数SQLConnect、SQLDriverConnect或SQLBrowseConnect连接相关的数据源;
  2. 调用函数SQLAllocHandle申请语句句柄,通过语句句柄应用程序可以执行SQL语句进行相关的SQL操作。调用函数SQLPrepare对SQL语句和操作进行准备,调用SQLDescribeCol、SQLDescribeParam等函数取得相关的描述信息,依据描述信息调用SQLBindCol、SQLBindParam等函数绑定相关的列和参数,然后调用SQLExecute执行SQL语句,实现相关的SQL操作。应用程序也可以调用函数SQLExecDirect直接执行SQL语句进行相关的SQL操作;
  3. 应用程序可以通过调用ODBC编目函数SQLTables、SQLColumns、SQLStatistics等取得数据源相关的字典信息;
  4. 如果连接属性自动提交选项设置为手动提交状态,应用程序可以调用函数SQLEndTran来提交或回滚事务,进行相关的事务处理;
  5. 调用函数SQLFreeHandle来释放申请的语句句柄;
  6. 调用函数SQLDisconnect来断开应用程序与数据源之间的连接;
  7. 调用函数SQLFreeHandle来释放申请的连接、环境句柄。

使用ODBC编程的基本步骤如下图所示:

直接使用ODBC函数开发应用程序的基本步骤

图3.6 直接使用ODBC函数开发应用程序的基本步骤

下面是一个调用DM ODBC 3.0的简单实例:

##include <windows.h>
##include <stdio.h>
##include <sql.h>
##include <sqltypes.h>
##include <sqlext.h>
/* 检测返回代码是否为成功标志,当为成功标志返回TRUE,否则返回FALSE */
##define RC_SUCCESSFUL(rc)	((rc) == SQL_SUCCESS || (rc) == SQL_SUCCESS_WITH_INFO)
/* 检测返回代码是否为失败标志,当为失败标志返回TRUE,否则返回FALSE */
##define RC_NOTSUCCESSFUL(rc) (!(RC_SUCCESSFUL(rc)))
HENV		 henv;	/* 环境句柄 */
HDBC		 hdbc;	/* 连接句柄 */
HSTMT		 hsmt;	/* 语句句柄 */
SQLRETURN	 sret;		/* 返回代码 */
char			szpersonid[11];		/*人员编号*/
SQLLEN 		cbpersonid=0;
char			szname[51];		/*人员姓名*/
SQLLEN		cbname=0;
char			szphone[26];		/*联系电话*/
SQLLEN		cbphone=0;
void main(void)
{
/* 申请一个环境句柄                   */
SQLAllocHandle(SQL_HANDLE_ENV, NULL, &henv);
/* 设置环境句柄的ODBC版本             */
SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3, SQL_IS_INTEGER);
/* 申请一个连接句柄                   */
SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc);
SQLConnect(hdbc, (SQLCHAR *)"DM", SQL_NTS, (SQLCHAR *)"SYSDBA", SQL_NTS, (SQLCHAR *)"SYSDBA", SQL_NTS);
/* 申请一个语句句柄                   */
SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hsmt);
/* 立即执行查询人员信息表的语句       */
SQLExecDirect(hsmt, (SQLCHAR *)"SELECT personid, name, phone FROM person.person;", SQL_NTS);
/* 绑定数据缓冲区                     */
SQLBindCol(hsmt, 1, SQL_C_CHAR, szpersonid, sizeof(szpersonid), &cbpersonid);
SQLBindCol(hsmt, 2, SQL_C_CHAR, szname, sizeof(szname), &cbname);
SQLBindCol(hsmt, 3, SQL_C_CHAR, szphone, sizeof(szphone), &cbphone);
/* 取得数据并且打印数据 */
printf("人员编号 人员姓名 联系电话\n");
for (;;) {
sret = SQLFetchScroll(hsmt, SQL_FETCH_NEXT, 0);
if (sret == SQL_NO_DATA_FOUND)
break;
printf("%s	%s	%s\n", szpersonid, szname, szphone);
}
/* 关闭游标,终止语句执行 */
SQLCloseCursor(hsmt);  
/* 释放语句句柄  */
SQLFreeHandle(SQL_HANDLE_STMT, hsmt);
/* 断开与数据源之间的连接 */
SQLDisconnect(hdbc);
/* 释放连接句柄 */
SQLFreeHandle(SQL_HANDLE_DBC, hdbc);
/* 释放环境句柄*/
SQLFreeHandle(SQL_HANDLE_ENV, henv);
}

3.5 使用存储过程和函数

DM允许用户创建和使用存储模块,下面介绍如何在DMODBC应用中使用存储过程和函数。

3.5.1 存储过程与函数字典信息的获取

DM ODBC 3.0支持字典函数SQLProcedures的调用,用户可以调用此函数来获取DM存储过程与函数的字典信息。

调用方法如下:

SQLProcedures(stmt, (SQLCHAR\*)"SYSTEM", SQL_NTS, (SQLCHAR\*)"SYSDBA", SQL_NTS,(SQLCHAR\*)"TEST_PROC",SQL_NTS);

返回字典信息格式如表3.4所列。

表3.4 字典信息说明表
编号 字典项 相关说明
1 PROCEDURE_CAT 存储模块编目信息
2 PROCEDURE_SCHEM 存储模块模式信息
3 PROCEDURE_NAME 存储模块名
4 NUM_INPUT_PARAMS DM暂时没有返回此项信息
5 NUM_OUTPUT_PARAMS DM暂时没有返回此项信息
6 NUM_RESULT_SETS DM暂时没有返回此项信息
7 REMARKS DM暂时没有返回此项信息
8 PROCEDURE_TYPE 存储模块的类型

DM ODBC 3.0支持字典函数SQLProcedureColums的调用,用于返回存储模块的参数信息。

调用方法如下:

SQLProcedureColumns(stmt,(SQLCHAR\*)"SYSTEM",SQL_NTS, (SQLCHAR\*)"SYSDBA",
SQL_NTS, (SQLCHAR\*)"TEST_PROC", SQL_NTS, NULL, 0);

返回字典信息格式如表3.5所列。

表3.5 字典信息说明表
编号 字典项 相关说明
1 PROCEDURE_CAT 存储模块编目信息
2 PROCEDURE_SCHEM 存储模块模式信息
3 PROCEDURE_NAME 存储模块名
4 COLUMN_NAME 参数名
5 COLUMN_TYPE 参数的类型,即为输入参数还是输出参数
6 DATA_TYPE 参数的SQL数据类型
7 TYPE_NAME 参数的类型名
8 COLUMN_SIZE 参数的精度
9 BUFFER_LENGTH 参数所占用的字符长度
10 DECIMAL_DIGITS 参数的刻度
11 NUM_PREC_RADIX 仅对数值类型有效,仅为10或者2,如果为10表示为精确数字,如果为2表示为非精确数字
12 NULLABLE 参数是否接收空值标志
13 REMARK 参数说明
14 COLUMN_DEF 参数的缺省值
15 SQL_DATA_TYPE 参数的SQL数据类型
16 SQL_DATETIME_SUB 日期时间类型或者时间间隔类型的子代码
17 CHAR_OCTET_LENGTH 字符数据类型以字节计算的最大长度,非字符类型返回空值
18 ORDINAL_POSITION 参数的顺序
19 IS_NULLABLE 参数是否包含空值

3.5.2 存储模块的创建

用户可以使用SQLExecDirect函数执行创建存储模块的SQL语句来创建存储模块,如下例所示:

SQLExecDirect(stmt, (SQLCHAR *)"create or replace procedure test_proc (c1 in int) as declare 
c2 int 
begin
c2 := c1 + 100;
end;", SQL_NTS);

3.5.3 存储模块的调用

调用存储模块的方法可以分为两种情况。

  1. 立即调用

如果存储过程需要设置参数,那么在调用存储模块的时候就已经为所有的IN、INOUT类型的参数进行了赋值,带有OUT属性的参数的值是通过取得存储过程结果集的方法获取的。立即执行存储过程的示例如下:

SQLExecDirect(hsmt,(SQLCHAR\*)"call test_proc(123);",SQL_NTS);
  1. 参数调用

这种调用方法指的是在调用模块的时候,其参数值用问号来代替,发送给服务器之后,服务器返回参数的准备信息,用户依据服务器返回的参数描述信息进行参数绑定,然后执行,其参数的处理方法与普通的参数处理方法相同。

DM支持存储模块返回多个结果集的处理,多结果集的切换通过SQLMoreResult函数切换,下面通过一个实例来说明如何使用。

##include <windows.h>
##include <stdio.h>
##include <sql.h>
##include <sqltypes.h>
##include <sqlext.h>
HENV	env;
HDBC	dbc;
HSTMT	stmt;
RETCODE	ret;
short	i;
short	cols;
char	colname[129];
char	coldata[256];
void main(void)
{
SQLAllocHandle(SQL_HANDLE_ENV, NULL, &env);
SQLSetEnvAttr(env, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3, SQL_IS_INTEGER);
SQLAllocHandle(SQL_HANDLE_DBC, env, &dbc);
SQLConnect(dbc, (SQLCHAR *)"DM", SQL_NTS, (SQLCHAR *)"SYSDBA", SQL_NTS, (SQLCHAR *)"SYSDBA", SQL_NTS);
SQLAllocHandle(SQL_HANDLE_STMT, dbc, &stmt);
SQLExecDirect(stmt, (SQLCHAR *)"drop table test_table1;", SQL_NTS);
SQLExecDirect(stmt, (SQLCHAR *)"drop table test_table2;", SQL_NTS);
SQLExecDirect(stmt, (SQLCHAR *)"create table test_table1 (t1col int);", SQL_NTS);
SQLExecDirect(stmt, (SQLCHAR *)"insert into test_table1 values(100);", SQL_NTS);
SQLExecDirect(stmt, (SQLCHAR *)"create table test_table2 (t2col varchar(10));", SQL_NTS);
SQLExecDirect(stmt, (SQLCHAR *)"insert into test_table2 values('hello!');", SQL_NTS);
SQLExecDirect(stmt, (SQLCHAR *)"create or replace procedure test_proc as begin select * from test_table1;select * from test_table2;end;", SQL_NTS);
SQLExecDirect(stmt, (SQLCHAR *)"call test_proc;", SQL_NTS);
SQLNumResultCols(stmt, &cols);
for (i=1; i<=cols; i++)
{
SQLDescribeCol(stmt, i, (SQLCHAR *)colname, 129, NULL, NULL, NULL, NULL, NULL);
printf("%s ", colname);
}
printf("\n");
for ( ; ; )
{
ret = SQLFetch(stmt);
if (ret == SQL_NO_DATA_FOUND) break;
for (i=1; i<=cols; i++)
{
SQLGetData(stmt, i, SQL_C_CHAR, coldata, 256, NULL);
printf("%s ", coldata);
}
printf("\n");
}
SQLMoreResults(stmt);
SQLNumResultCols(stmt, &cols);
for (i=1; i<=cols; i++)
{
SQLDescribeCol(stmt, i, (SQLCHAR *)colname, 129, NULL, NULL, NULL, NULL, NULL);
printf("%s ", colname);
}
printf("\n");
for ( ; ; )
{
ret = SQLFetch(stmt);
if (ret == SQL_NO_DATA_FOUND) break;
for (i=1; i<=cols; i++)
{
SQLGetData(stmt, i, SQL_C_CHAR, coldata, 256, NULL);
printf("%s ", coldata);
}
printf("\n");
}
SQLFreeHandle(SQL_HANDLE_STMT, stmt);
SQLDisconnect(dbc);
SQLFreeHandle(SQL_HANDLE_DBC, dbc);
SQLFreeHandle(SQL_HANDLE_ENV, env);
} 
微信扫码
分享文档
扫一扫
联系客服