DB编译问题记录

轻松实现应用程序移植!

是否计划将 Oracle 应用程序移植到 IBM® DB2® for Linux®, UNIX® and Windows®?学习如何使用本文描述的步骤完成此任务。本文包含的示例脚本可以使任务更加简单。
简介

本文适用于以下开发人员、管理员或独立软件供应商(Independent Software Vendor,ISV):

拥有支持非 IBM 数据库(比如 Oracle Server)的数据库应用程序 
其客户希望将 Oracle 应用程序迁移到位于分布式平台上的 IBM DB2 — DB2 for Linux, UNIX, and Windows(本文中使用 DB2) 
许多想迁移 Oracle 应用程序以支持 DB2 的企业和业务合作伙伴提出了以下问题: 

许多使用 Oracle 服务器的客户都希望在 DB2 上运行他们的应用程序。我需要进行哪些更改才能使应用程序支持 DB2 数据访问? 

只要逐步执行本文描述的步骤,就很容易将 Oracle 应用程序移植到 DB2 平台。本文指出了在将 Oracle 应用程序迁移到 DB2 时可能遇到的最常见问题,同时也提供了克服这些问题的步骤。我曾经成功对拥有 1 亿行源代码的 Oracle 应用程序进行了概念证明(proof-of-concept)迁移,本文就是建立在这个基础之上。

以下是针对本文的示例迁移的一些假设:

示例 Oracle 应用程序是用 C/C++ 编程语言编写的。 
在 UNIX (AIX® 5.2) 平台上的 DB2 Version V8.2 上进行移植。本文讨论的大多数问题也适用于 DB2 9。 
以下是本文将描述的问题:

DB2 预编译器不能识别声明部分中用户定义的数据类型(typefef,#define macros),位于 EXEC SQL BEGIN DECLARE SECTION 和 EXEC SQL END DECLARE SECTION 语句之间。 
由于参数类型不同,使用用户定义函数(UDF)(比如 ||、rawtohex、hextoraw,等等)开发的应用程序不能在 DB2 上编译。 
使用 DECODE 函数的 Oracle 应用程序不能在 DB2 上正确编译。 
需要在 DB2 中执行与包含 NOWAIT 的 Oracle SQL 语句类似的行为。 
需要在 DB2 中处理大小超过 32672 的主机变量。 
当移植到 DB2 时,需要考虑 Oracle 的 “Select for update” 语句。 
本文将移植过程分解成一些主要任务,然后探讨每个任务涉及的问题。

任务 1:标识嵌入式 SQL(.sqC)程序

对于此任务,需要在应用程序中标识所有的 Pro C(SQL + C/C++ 程序组合)程序。首先应该将这些 Pro C 程序转换为 DB2 能用其预编译器解释的嵌入式 SQL 程序。 

SQL 与 C 程序组合:扩展名为 .sqc(在 UNIX 上) 
SQL 与 C++ 程序组合:扩展名为 .sqC(在 UNIX 上) 
也许还需要回顾一下用 C/C++ 开发的嵌入式 SQL 程序示例。可以在 DB2 实例目录下的以下路径中找到它们: 

C 程序:sqllib/samples/c 
C++ 程序:sqllib/samples/cpp 
这些嵌入式 SQL 程序是用 DB2 的预编译器(db2 prep)编译的。 

问题 1

如果在 EXEC SQL BEGIN DECLARE SECTION 和 EXEC SQL END DECLARE SECTION 语句之间使用了用户定义的数据类型(例如 C/C++ 中的 “typedef”)和宏(#define),那么 DB2 预编译器在编译这些程序时会遇到一些问题。

因此,如果将一个包含嵌入式 SQL 语句的文件(.sqC)传递到 db2 precompile 命令,则编译时会提示出错。如清单 1 所示。

注:
此示例只是为了描述问题并在编译之后生成相应的错误,所以其形式非常简单。要重现此场景,可以执行 db2 "create table test_table (dept int)" 命令创建一个 “TEST_TABLE” 表。  

考虑如下的 test1.sqC 文件: 

清单 1. test1.sqC
                
#include 

#define LONGBIG long

/* 
Or we can have a definition like:
typedef long LONGBIG
*/

EXEC SQL INCLUDE SQLCA;
EXEC SQL BEGIN DECLARE SECTION;
        LONGBIG col1_val;
EXEC SQL END DECLARE SECTION;

int main()
{
        EXEC SQL SELECT DEPT 
        INTO :col1_val 
        FROM TEST_TABLE;
        
        return 0;
}      

当试图使用 db2 prep 编译 test1.sqC 文件时,会得到以下错误:

清单 2. 预编译 test1.sqC 的结果
                
db2 prep test1.sqC bindfile 

LINE    MESSAGES FOR test1.sqC
------  --------------------------------------------------------------------
        SQL0060W  The "C++" precompiler is in progress.
  11  SQL0008N  The token "LONGBIG" found in a host variable
                  declaration is not valid.
  18  SQL4942N  The statement selects an incompatible data type
                  into host variable ":col1_val".  SQLSTATE=42806
        SQL0095N  No bind file was created because of previous
                  errors.
        SQL0091W  Precompilation or binding was ended with "3"
                  errors and "0" warnings.

[db2inst1]/users/ganesh_gosavi/mig1/issues/NULL_issue>
      

出现这些错误是因为 DB2 预编译器不能解析一些 typedef 和宏,这些 typedef 和宏用于通过 EXEC SQL BEGIN DECLARE SECTION 和 EXEC SQL END DECLARE SECTION 语句定义的声明部分。 

问题 2

第 2 个问题与在代码声明部分中广泛使用的类型定义有关,这些类型定义包括 typedef 结构、嵌入式结构中的 typedef、用户定义的数据类型,以及在各种嵌入式头文件中声明的 typedef。

清单 3 中的示例源代码演示了该问题。

清单 3. test2.sqC
                                                
#include 
#include 
.
.

EXEC SQL INCLUDE SQLCA;

EXEC SQL BEGIN DECLARE SECTION;
#include "extrndef.h"
        EXEC SQL INCLUDE 'UPR_ELEMENTS.H';

        EXTERN varchar b2k_amount_host_str[50][35];
        static varchar cur_time[20];
        static varchar cur_user[16];

        static char cur_time[20];
        static char cur_user[16];
EXEC SQL END DECLARE SECTION; 
        

问题 1 和 2 的解决方案

以上问题的一个常用解决方案是,不要在 EXEC SQL 声明部分中使用 typedef 和宏。但是如果拥有很多这样的文件,手动完成此操作很麻烦,您也许希望进行自动处理。我们在此处提供一个 Perl 脚本(esql_prep),以减少手动操作的麻烦,该脚本使任务变得更加简单。

注:该脚本的主要开发人员是 Knut Stolze ([url=mailto:stolze@de.ibm.com]stolze@de.ibm.com[/url])。请谨慎使用此脚本,跟平常一样,在将其应用到源代码中之前,请首先测试其功用。也许有一些特殊条件未包含在脚本中,这可能会产生错误的结果。 

使用此 Perl 脚本的语法如下:

清单 4. Perl 脚本的语法
                        
./esql_prep xxxxxxx.SQX "gcc -E -I" 

此处,

“path” 既可以是头(.h)包含文件的相对路径,也可以是其绝对路径。 
“xxxxxxx.SQX” 是一个含有问题 1 和问题 2 中所描述问题的文件。 
“gcc -E” 选项告诉 C++ 编译器对 “xxxxxxx.SQX” 文件进行预处理。 

上面的命令对 xxxxxxx.SQX 文件进行预处理,并生成一个新文件 xxxxxxx.SQC。在 xxxxxxx.SQC 文件中可以看到,预定义的宏和用户定义类型已经被替换了。然后可以将 xxxxxxx.SQC 文件传递给 db2 prep 预编译器执行下一步操作。 

问题 3

当将嵌入式 SQL 程序(例如,test3.sqc 或 test3.sqC)传递给 db2 prep 预编译器时,会输出两个文件:

一个修改过的 C 或 C++ 程序(test.c 或 test.C) 
一个绑定文件(test.bnd) 
当将 test3.c 或 test3.C 传递给语言编译器(C/C++)时,语言编译器有时会返回一个与清单 5 类似的错误: 

清单 5. test3.sqC 编译错误
                
"test3.i",line 6043.38:1540-0274 (S)The name lookup for "NULL" did not find a declaration.
        

此错误是由于预编译器在创建 C 文件时插入与清单 6 类似的语句引起的: 

清单 6. 预编译器输出
                
.
.
sql_setdlist[0].sqldata = (void*)&col1_val;
#line 6043 "test3.i"
      sql_setdlist[0].sqlind = 0L;
#line 6043 "test3.i"
      sqlasetdata(3,0,1,sql_setdlist,NULL,0L);
    }        

此时发生了如下操作?

上面的 esql_prep 通过 C/C++ 预编译器运行源代码(仍包含嵌入式 SQL 语句)。结果,所有的 #include 指令都被解析了,包括  中的指令。 
DB2 预编译器插入这些语句(包含 “NULL”),如上面的清单所示。 
常规的 C/C++ 编译器开始编译。它执行一个常规的 C/C++ 预编译。但是预编译不会做任何事情,因为这些都在步骤 1 中完成了。特别是,不再有 #include 指令。结果,代码中的 “NULL” 未被更改。C/C++ 编译器找到 “NULL” 并报告错误,因为它不知道该怎么做。毕竟 “#define NULL (void *)0” 不在此处,因为没有找到 #include 。 
因此,必须为 NULL 提供一个常规的 #define。 

结果,db2 prep 预编译器返回清单 4 所示的错误。

问题 3 的解决方案

要消除此错误,可以使用编译器的 -D 选项。

例如:

清单 7. test2.sqC
                        
/usr/vacpp/bin/xlC  -c  -o test3.o -DNULL=0  -I./  -I/db2/db2inst1/sqllib/include

任务 2:在 DB2 for Linux, UNIX, and Windows 中实现 Oracle UDF 行为

这是在将 Oracle 应用程序迁移到 DB2 for Linux, UNIX, and Windows 时面临的主要挑战。应用程序调用 Oracle 支持的各种 UDF。对各个文件和目录的所有代码(可能有数百万条)进行检查是非常困难的。找到每个位置并更改相应的源代码,从而让应用程序支持 DB2,这是一项单调、麻烦且容易出错的工作。

本文下载部分的 OracleToDB2UDFs.zip 提供了许多等价的 Oracle UDF。只需在选择的模式下对它们进行注册即可。无需修改应用程序源代码就可使用这些 UDF。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值