轻松实现应用程序移植!
是否计划将 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 <stdio.h>
#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_iss
ue>
出现这些错误是因为 DB2 预编译器不能解析一些 typedef
和宏,这些 typedef 和宏用于通过 EXEC SQL BEGIN
DECLARE SECTION 和 EXEC SQL END DECLARE SECTION 语
句定义的声明部分。
问题 2
第 2 个问题与在代码声明部分中广泛使用的类型定义有关
,这些类型定义包括 typedef 结构、嵌入式结构中的
typedef、用户定义的数据类型,以及在各种嵌入式头文件
中声明的 typedef。
清单 3 中的示例源代码演示了该问题。
清单 3. test2.sqC
#include <stdio.h>
#include <string.h>
.
.
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
(stolze@de.ibm.com)。请谨慎使用此脚本,跟平常一样,
在将其应用到源代码中之前,请首先测试其功用。也许有
一些特殊条件未包含在脚本中,这可能会产生错误的结果
。
使用此 Perl 脚本的语法如下:
清单 4. Perl 脚本的语法
./esql_prep xxxxxxx.SQX "gcc -E -I<path>"
此处,
“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 指令都
被解析了,包括 <stdio.h> 中的指令。
DB2 预编译器插入这些语句(包含 “NULL”),如上面的
清单所示。
常规的 C/C++ 编译器开始编译。它执行一个常规的 C/C++
预编译。但是预编译不会做任何事情,因为这些都在步骤
1 中完成了。特别是,不再有 #include 指令。结果,代
码中的 “NULL” 未被更改。C/C++ 编译器找到 “NULL”
并报告错误,因为它不知道该怎么做。毕竟 “#define
NULL (void *)0” 不在此处,因为没有找到 #include
<stdio.h>。
因此,必须为 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。