轻松实现应用程序移植!
是否计划将 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。
是否计划将 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。