中国IT动力,最新最全的IT技术教程
最新100篇 | 推荐100篇 | 专题100篇 | 排行榜 | 搜索 | 在线API文档
首 页 | 程序开发 | 操作系统 | 软件应用 | 图形图象 | 网络应用 | 精文荟萃 | 教育认证 | 硬件维护 | 未整理篇 | 站长教程
ASP JS PHP工程 ASP.NET 网站建设 UML J2EESUN .NET VC VB VFP 网络维护 数据库 DB2 SQL2000 Oracle Mysql
服务器 Win2000 Office C DreamWeaver FireWorks Flash PhotoShop 上网宝典 CorelDraw 协议大全 网络安全 微软认证
硬件维护  CPU  主板  硬盘  内存  显卡  显示器  键盘鼠标  声卡音箱  打印机  机箱电源  BIOS  网卡  C#  Java  Delphi  vs.net2005
  当前位置:> IBM专区 > DB2 > SQL (Structured Query Language
将 Oracle Pro*C 程序转换为 DB2
作者:佚名 时间:2005-08-30 16:28 出处:互连网 责编:小渔
              摘要:将 Oracle Pro*C 程序转换为 DB2 Universal Database 应用程序
Marina Greenstein
认证的顾问 I/T 专家 - 数据管理, IBM White Plains
2003 年 10 月
对数据库的更改通常意味着对应用程序的更改。本文解释了在将 Oracle Pro*C 程序转换为带有嵌入式 DB2 SQL 调用的 C 程序的过程中所需的一些步骤。

简介
虽然近几年来 IBM® DB2® Universal Database™  应用程序开发经历了一些变更( 从 C/COBOL/Java™ 到 SQL 过程语言的存储过程,对用户定义函数中 SQl PL 的支持,触发器和内联 SQL,增强的内建函数集,等等),但是对将 SQL 嵌入到其他宿主语言(C/C++)的支持实际上一直都没有变。这导致了在将 Oracle Pro*C 程序转换到 DB2 UDB 以及将应用程序从 Oracle 移植到 DB2 UDB 过程中的很多困难。

本文解释了在将 Pro*C 程序转换为带有嵌入式 DB2 SQL 调用的 C 程序的过程中所需的一些步骤。

注意:本文所包括的例子是从实际程序中摘录而来的,本身不能通过编译。

连接到数据库 
不同环境下 C 程序连接到数据库的方式有所差异。在 Oracle 中,每个实例(服务名)只能管理一个数据库。DB2 实例可用于管理多个数据库。因而,连接语句应该显式地提供数据库的名称。为了连接到 Oracle 数据库,您需要指定 Oracle 用户和该用户的密码:


            EXEC SQL CONNECT :user_name IDENTIFIED BY :password;
            

在 DB2 中,您需要指定数据库名称、用户 ID 和该用户 ID 的密码。因此上述语句就转换为:


            EXEC SQL CONNECT TO :dbname USERID :userid PASSWORD : password;
            

请注意,需要将 dbnameuseridpassword 声明为宿主变量(host variable)。

宿主变量声明
宿主变量是指在 SQL 语句中引用到的 C 或 C++ 语言变量。这些变量允许应用程序将输入数据传递给数据库管理器,或者从数据库管理器接收输出数据。在对应用程序进行预编译之后,编译器将宿主变量当作任何其他 C/C++ 变量使用。

宿主变量不仅应该与 DB2 数据类型兼容(被 DB2 预编译器接受),而且还应该被编程语言编译器接受。

当 C 程序使用宿主变量操纵来自表中的值时,第一步是将 Oracle 表定义转换为 DB2 数据类型(参见下面的 表 1)。请注意,这是一对多的映射,因为它取决于对数据的实际使用。例如,如果 Oracle DATE 只存储实际的数据,那么它可以转换为 DB2 DATE,但是如果它存储 DATE 和 TIME,则需要将它转换为 DB2 TIMESTAMP

接下来的一步是用 C 数据类型匹配 DB2 数据类型。 表 1 显示了数据类型之间的映射:

表 1. Oracle 表定义到 DB2 数据类型

Oracle 数据类型 DB2 数据类型 C 数据类型声明
CHAR(n) CHAR(n) char char_var[n+1];
VARCHAR2(n) VARCHAR(n) struct{short len;
char data[n]} varchar_var;
or
char varchar_var[n+1];
LONG CLOB(2GB) SQL TYPE IS CLOB(n) v-name;
NUMBER(p,s) NUMERIC(p,s) double num_var;
DECIMAL(p,s) DECIMAL(p,s) double dec_var;
!note that passing parameters using
the SQLDA allows an exact representation
of DECIMAL/NUMERIC using packed BCD encoding.
INTEGER INTEGER sqlint32 int_var;
SMALLINT SMALLINT sqlint16 int_var;
    BIGINT sqlint64 bigint_var;
RAW(n) CHAR(n) FOR BIT DATA char ... [n]
LONG RAW BLOB(2GB) SQL TYPE IS BLOB(n) v_name;
DATE TIMESTAMP char tms_var[27];
DATE (only the date) DATE (MM/DD/YYYY) char dt_var[11];
DATE(only the time) TIME (HH24:MI:SS) char tm_var[16];

C 程序中的所有宿主变量都需要在一个专门的块中声明,以便 DB2 预编译器能够识别宿主变量以及它们的数据类型。


            EXEC SQL BEGIN DECLARE SECTION;
            char emp_name[31] = {'\\0'};
            sqlint32  ret_code = 0;
            EXEC SQL END DECLARE SECTION;
            

在这个声明块中,针对宿主变量数据类型的一些规则不同于 Oracle 预编译器中的规则。Oracle 预编译器允许将宿主变量声明为 VARCHARVARCHAR[n] 是 Pro*C 预编译器能够识别的伪类型。它用于表示用空格填充的、长度变化的字符串。Pro*C 预编译器将把它转换为由一个 2 字节长字段,后面跟一个 n-字节的字符数组组成的结构。DB2 要求使用标准 C 构造。因此,对变量 emp_name VARCHAR[25] 的声明需要转换为:


            struct emp_name 	{
            short  var_len;
            char   var_data[25] 	};
            

或者,如前所述,对 VARCHAR 数据使用 " char emp_name[n] " 也是允许的。PRO*C 中用户定义类型(使用 typedef)的变量需要转换为源数据类型。例如,类型 theUser_t 以前是 Oracle 对象类型,现在被声明为宿主变量:


            typedef struct  user_s
            {short int userNum;
            char userName[25];
            char userAddress[40];
            } theUser_t;
            

在 Pro*C 程序中,您可以将宿主变量声明为 theUser_t


            EXEC SQL BEGIN DECLARE;
            theUser_t *myUser;
            EXEC SQL END DECLARE SECTION;
            

为了将该宿主变量用于 DB2,您需要将其从 EXEC SQL DECLARE SECTION 取出并将宿主变量 MyUser  定义为一个结构。

DB2 允许将宿主变量声明为一个指针,不过有以下限制:

  • 如果宿主变量被声明为指针,则在同一个源文件中不能用同样的名称再去声明其他的宿主变量。
  • 宿主变量声明 char *ptr 是可以接受的,但这并不是指 以零字符结尾的长度不确定的字符串 。相反,它指的是 指向一个固定长度的、单字符的宿主变量的指针。这可能并不是 Oracle 宿主变量声明所希望达到的目的。

我们建议为 INTEGERBIGINT 分别使用 sqlint32sqlint64 。在默认情况下,对于那些 long 为 64 位数的平台,例如 64-位 UNIX®,对 long 宿主变量的使用将造成预编译器错误 SQL0402。使用 PREP 选项 LONGERROR NO 强迫 DB2 将 long 作为可接受的宿主变量类型来接受,并把它们当作 BIGINT变量。

Oracle 宿主表
在 Pro*C 程序中,可以使用数组声明宿主变量,然后声明一个您希望从中获取结果的游标。接着可以产生一个 fetch 语句,从该游标中取出所有的行并存放到那个宿主数组中。

下面来自 PRO*C 的一个代码片断演示了这个方法:


            EXEC SQL BEGIN DECLARE SECTION;
            long int     dept_numb[10];
            char         dept_name[10][14];
            char         v_location[12];
            EXEC SQL END DECLARE SECTION;
            /* ... */
            EXEC SQL DECLARE CUR1 CURSOR FOR
            SELECT DEPTNUMB, DEPTNAME
            FROM org_table
            WHERE LOCATION = :v_location;
            /*.... */
            EXEC SQL FETCH CUR1 INTO :dept_num, :dept_name;
            

最后一条语句将从游标中取出 10 行放入数组中。

由于 DB2 并不支持在宿主变量声明中使用数组,因此上述代码需要转换为:


            EXEC SQL BEGIN DECLARE SECTION;
            sqlint32     h_dept_numb = 0;
            char         h_dept_name[14] = {'\\0'};
            char         v_location[12] = {'\\0'};
            EXEC SQL END DECLARE SECTION;
            /* move array out of DECLARE section - just C variables */
            long  int     dept_numb[10];
            char          dept_name[10][14];
            short int     i = 0;
            /* ... */
            EXEC SQL DECLARE CUR1 CURSOR FOR
            SELECT DEPTNUMB, DEPTNAME
            FROM org_table
            WHERE LOCATION = :v_location;
            /*we need Fetch one row at the time and move to corresponding
            member of array */
            for (i=0;i<11;i++){
            EXEC SQL FETCH CUR1 INTO :h_dept_num, :h_dept_name;
            if (SQLCODE == 100){
            break;
            }
            dept_numb[i] = h_dept_numb;
            strcpy(dept_name[i], h_dept_name);
            }
            

异常处理
Oracle 与 DB2 的错误捕捉机制非常相似,两者都使用将错误例程从主线逻辑分离出来的概念。在 DB2 中,有三种不同的 WHENEVER 语句可用于定义出现错误情况下的程序行为:


            EXEC SQL WHENEVER SQLERROR GOTO error_routine;
            EXEC SQL WHENEVER SQLWARNING CONTINUE;
            EXEC SQL WHENEVER NOT FOUND not_found_routine;
            

虽然 WHENEVER 语句像其他 SQL 语句一样以 EXEC SQL 作为前缀,但它并不是可执行的语句。相反,WHENEVER 语句会引起预编译器在每条 SQL 语句之后生成程序中的代码,并执行在 WHENEVER 语句中指定的动作。SQLERROR 意味着某一条 SQL 语句返回一个非正的 SQLCODE,用于表示一个错误情况。SQLWARNING 表示一个非负的 SQLCODE (除了 +100),而 NOT FOUND 指定 SQLCODE = +100,表示没有找到符合请求的数据行。一个编译单元可以包含尽可能多的 WHENEVER 语句,并且这些语句可以放在程序中的任何地方。一条 WHENEVER 语句的作用域很广,从该语句在文件中所在的地方,到文件的字符流之内,一直到下一条合适的 WHENEVER 语句出现或者到达文件的结尾处。在那样的分析中没有考虑函数或程序块。例如,您可能有两条不同的 SELECT 语句,其中一条必须返回至少一行数据,而另一条可以不返回任何数据。您将需要两条不同的 WHENEVER 语句:


            EXEC SQL WHENEVER NOT FOUND GOTO no_row_error;
            EXEC SQL SELECT  address
            INTO  :address
            FROM   test_table
            WHERE phone = :pnone_num;
            .....
            EXEC SQL WHENEVER NOT FOUND CONTINUE;
            EXEC SQL SELECT  commis_rate
            INTO :rate :rateind
            WHERE prod_id = :prodId;
            if (rateind == -1 ) rate = 0.15;
            ...
            

Oracle 预编译器还支持 DO 和 STOP 语句作为 WHENEVER 语句中的动作。但是 DB2 预编译器并不支持这两种语句,而需要将其转换为 GOTO 语句。

另一种替代的做法是在每条 EXEC SQL 语句之后显式地检查 SQLCODE,因为这将允许更多上下文敏感的错误处理。

错误消息和警告
DB2 中的 SQL Communication Area (SQLCA) 数据结构与 Oracle 中的类似。SQLCA 提供了用于诊断性检查和事件处理的信息。

为了获得全文本的更长的(或嵌套的)错误消息,您需要 sqlglm() 函数:


            sqlglm(message_buffer, &buffer_size, &message_length);
            

其中 message_buffer 是字符缓冲区,您希望 Oracle 将错误消息存放在其中。 buffer_size 指定 message_buffer 的大小,单位为字节。Oracle 将错误消息的实际长度存储在 message_length 中。Oracle 错误消息的最大长度是 512 字节。

DB2 Universal Database 为它的用户提供了一个特殊的运行时 API 函数,用以返回一条基于 SQLCODE 的错误消息:


            rc=sqlaintp(msg_buffer, 1024, 80, sqlca.sqlcode);
            

其中 "80" 代表字符数,在消息中每过这么多字符就要插入一个换行符(line break)。DB2 将搜索工作边界以便插入那样一个换行符;"1024" 指定了消息缓冲区的长度;例如, char msg_buffer[1024]

调用这个函数后,被分配空间的缓冲区将包含描述性错误消息,例如:


            SQL0433N Value "TEST VALUES" is too long. SQLSTATE=22001.
            

如果您需要更多关于某一特定错误的信息,DB2 Universal Database 提供了一个 API 函数,该函数返回与特定 SQLSTATE 相关的经过扩展的消息:


            rc=sqlogstt(msg_sqlstate_buffer, 1024, 80, sqlca.sqlcode);
            

调用该函数后, char msg_sqlstate_buffer[1024] 将包含以下消息:


            SQLSTATE 22001: Character data, right truncation occurred; for example, an update or insert
            value is a string that is too long for the column, or datetime value cannot be assigned to a
            host variable, because it is too small.
            

从 C 程序中传递数据给存储过程
在 Oracle 中,为了调用一个远程数据库过程,可以使用以下语句:


            EXEC SQL EXECUTE
            BEGIN
            Package_name.SP_name(:arg_in1,:arg_in2, :status_out);
            END;
            END-EXEC;
            

在调用环境和存储过程之间值的传输可以通过参数达到。您可以为每个参数选择三种模式中的一种:IN、OUT 或 INOUT。例如,上面的存储过程可以声明为:


            CREATE PACKAGE package_name IS
            PROCEDURE  SP_name(
            agr_in1 IN NUMBER ,
            arg_in2 IN CHAR(30),
            status_out OUT NUMBER);
            

当该存储过程被调用时,从调用程序传递过来的值将被存储过程相应地接收。

DB2 客户机应用程序通过使用 CALL 语句来调用存储过程。CALL 语句可以传递参数给存储过程,并接受从存储过程返回的参数。它的语法如下:


            CALL procedure_name  (:parm1, ?parmN);
            

与所有 SQL 语句一样,先用参数标记准备好 CALL 语句,然后再使用 SQLDA 为标记提供值:


            CALL procedure_name USING DESCRIPTOR host_var;
            

如果您有数量不确定的宿主变量,或者有很多变量(100 或更多),那么 SQLDA 将很有帮助。否则在那些情况下管理这么多变量是很麻烦的。

为了从 C 客户机上调用存储过程,首先需要完成以下任务:

  • 需要创建一个存储过程,并将其注册到数据库。
  • 对于该存储过程的每个 IN 和 INOUT 参数,应该声明和初始化一个宿主变量或参数标记。

考虑一个例子。对于那些当前薪水低于某个值的每一个雇员,程序必须给他加薪。程序将传递那个值给存储过程,执行一次更新,然后返回状态。用 C 编写的客户机代码看上去应该是这样的:


            #include <sqlenv.h>
            main()
            {
            EXEC SQL BEGIN DECLARE SECTION;
            Sqlint32  salary_val=0;
            Sqlint16 salind=1;
            Sqlint16  status=0;
            Sqlint16  statind=0;
            EXEC SQL END DECLARE SECTION;
            EXEC SQL INCLUDE SQLCA;
            EXEC SQL CONNECT TO sample;
            EXEC SQL WHENEVER SQLERROR GOTO err_routine;
            salary_val = getSalaryForRaise();
            statind = -1;   /*  set indicator variable to -1 for
            status
            as output-only variable */
            EXEC SQL CALL  raiseSal(:salary_val :salind, :status :statind);
            if (status == 0){
            printf  (" The raises has been successfully given \\n ");
            EXEC SQL COMMIT;
            }
            else
            if (status ==1)
            printf  (" NO input  values has been provided.\\n ");
            else
            if(status == 2)
            printf("Stored procedure failed.\\n");
            err_routine:
            printf  (" SQL Error, SQLCODE =  \\n ", SQLCODE);
            EXEC SQL ROLLBACK;
            }
            

注意,在 CALL 语句中使用的所有宿主变量都是在 EXEC SQL DECLARE SECTION 中声明和初始化的。

构建一个 C/C++ DB2 应用程序
DB2 提供了用于预编译、编译和链接 C-embedded SQL 程序(在 C 中嵌入 SQL 的程序)的构建脚本(build script)。这些脚本位于 sqllib/samples/c 目录中,与可以用这些文件构建的示例程序放在一起。这个目录还包含 embprep 脚本,该脚本用在构建脚本内,用于预编译一个 *.sqc 文件。

DB2 为受支持平台上的每种语言提供了构建文件,在受支持平台上提供了它们构建的各种程序,与用于每个语言的示例程序放在同一个目录中。除非特别指明,否则这些构建文件是用于所有受支持平台上的各种受支持语言的。在 Windows® 上,构建文件有 .bat (batch) 扩展名,在 UNIX 平台上,构建文件没有扩展名。例如, bldmapp.bat 是一个用于构建 Windows 上的 C/C++ 应用程序的脚本。 

DB2 还提供了 utilemb.sqc utilemb.h 文件,这两个文件包含了用于错误处理的函数。为了使用这些实用函数(utility function),必须首先编译实用文件(utility file),然后再在创建可执行的目标程序的过程中链接它的目标文件。在示例目录中的 makefile 文件和构建文件都会为需要错误检查实用查询的程序做这些工作。

要了解更多关于构建 C 应用程序的信息,参见 DB2 UDB V8 应用程序开发指南:构建和运行应用程序 (ISBN SC09-4825-00)。

结束语
本文讨论了如何更改一个 C 程序,使其访问 DB2 Universal Database,而不是 Oracle 数据库。即使对于 C/C++ 开发来说嵌入 Oracle 和 DB2 UDB 数据库访问调用在原理上是类似的,但是两者之间的一些特定差别还是可能带来挑战。本文试图帮助开发者理解这些差异,并克服由这些差异带来的挑战。

关闭本页
 
首页 | 投资与合作 | 服务条款 | 隐私政策 | 收藏本站 | 设为首页 | 新用户注册 | 免责声明 | 使用帮助
Copyright ©2005-2008 chinaitpower.com All rights reserved. www.chinaitpower.com 版权所有