

本文属于机器翻译版本。若本译文内容与英语原文存在差异，则一律以英文原文为准。

# 将遗留应用程序从 Oracle Pro\*C 迁移到 ECPG
<a name="migrate-legacy-applications-from-oracle-pro-c-to-ecpg"></a>

*Sai Parthasaradhi 和 Mahesh Balumuri，Amazon Web Services*

## Summary
<a name="migrate-legacy-applications-from-oracle-pro-c-to-ecpg-summary"></a>

大多数具有嵌入式 SQL 代码的遗留应用程序使用 Oracle Pro\*C 预编译器来访问数据库。当您将这些 Oracle 数据库迁移到 Amazon Relational Database Service (Amazon RDS) for PostgreSQL 或 Amazon Aurora PostgreSQL-Compatible Edition 时，您必须将应用程序代码转换为与 PostgreSQL 中的预编译器兼容的格式（称为 ECPG）。此模式描述了如何将 Oracle Pro\*C 代码转换至 PostgreSQL ECPG 中的等效代码。 

有关 Pro\*C 的更多信息，请参阅 [Oracle 文档](https://docs.oracle.com/cd/E11882_01/appdev.112/e10825/pc_01int.htm#i2415)。有关 ECPG 的简要介绍，请参阅[其他信息](#migrate-legacy-applications-from-oracle-pro-c-to-ecpg-additional)部分。

## 先决条件和限制
<a name="migrate-legacy-applications-from-oracle-pro-c-to-ecpg-prereqs"></a>

**先决条件**
+ 一个有效的 Amazon Web Services account
+ Amazon RDS for PostgreSQL 或 Aurora PostgreSQL-Compatible 数据库
+ 本地运行的 Oracle 数据库

## 工具
<a name="migrate-legacy-applications-from-oracle-pro-c-to-ecpg-tools"></a>
+ 下一部分中列出的 PostgreSQL 程序包。
+ [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-welcome.html) – AWS 命令行界面（AWS CLI）是一种开源工具，用于通过命令行 Shell 中的命令与 Amazon Web Services 交互。仅需最少的配置，即可使用 AWS CLI 开始运行命令，以便从终端程序中的命令提示符实现与基于浏览器的 AWS 管理控制台所提供的功能等同的功能。

## 操作说明
<a name="migrate-legacy-applications-from-oracle-pro-c-to-ecpg-epics"></a>

### 在 CentOS 或 RHEL 设置构建环境
<a name="set-the-build-environment-on-centos-or-rhel"></a>


| Task | 说明 | 所需技能 | 
| --- | --- | --- | 
| 安装 PostgreSQL 程序包。 | 通过使用以下命令安装所需 PostgreSQL 程序包。<pre>yum update -y<br />yum install -y yum-utils<br />rpm -ivh https://download.postgresql.org/pub/repos/yum/reporpms/EL-8-x86_64/pgdg-redhat-repo-latest.noarch.rpm<br />dnf -qy module disable postgresql</pre> | 应用程序开发者、 DevOps 工程师 | 
| 安装标头文件和库。 | 使用以下命令安装包含头文件和库的 `postgresql12-devel` 程序包。在开发环境和运行时系统环境中都安装该包，以避免运行时系统环境中出现错误。<pre>dnf -y install postgresql12-devel<br />yum install ncompress zip ghostscript jq unzip wget git -y</pre><br />仅对于开发环境，还运行以下命令。<pre>yum install zlib-devel make -y<br />ln -s /usr/pgsql-12/bin/ecpg /usr/bin/</pre> | 应用程序开发者、 DevOps 工程师 | 
| 配置环境路径变量。 | 为 PostgreSQL 客户端库设置环境路径。<pre>export PATH=$PATH:/usr/pgsql-12/bin</pre> | 应用程序开发者、 DevOps 工程师 | 
| 必要时安装其他软件。 | 如果需要，可以在 Oracle 中安装 **pgLoader** 以替换 **SQL\*Loader**。<pre>wget -O /etc/yum.repos.d/pgloader-ccl.repo https://dl.packager.io/srv/opf/pgloader-ccl/master/installer/el/7.repo<br />yum install pgloader-ccl -y<br />ln -s /opt/pgloader-ccl/bin/pgloader /usr/bin/</pre><br />如果您要从 Pro\*C 模块调用任何 Java 应用程序，请安装 Java。<pre>yum install java -y</pre><br />安装 **ant** 以编译 Java 代码。<pre>yum install ant -y</pre> | 应用程序开发者、 DevOps 工程师 | 
| 安装 Amazon CLI。 | 安装 AWS CLI 以运行命令，从您的应用程序与 Amazon Web Services （例如 AWS Secrets Manager 和 Amazon Simple Storage Service (Amazon S3)）交互。<pre>cd /tmp/<br />curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"<br />unzip awscliv2.zip<br />./aws/install -i /usr/local/aws-cli -b /usr/local/bin --update</pre> | 应用程序开发者、 DevOps 工程师 | 
| 确定要转换的程序。 | 确定要从 Pro\*C 转换为 ECPG 的应用程序。 | 应用程序开发人员、应用程序所有者 | 

### 将 Pro\*C 代码转换为 ECPG
<a name="convert-pro-c-code-to-ecpg"></a>


| Task | 说明 | 所需技能 | 
| --- | --- | --- | 
| 删除不需要的标头。 | 删除 PostgreSQL 中不需要的 `include ` 标头，例如 `oci.h`、`oratypes` 和 `sqlda`。 | 应用程序所有者、应用程序开发人员 | 
| 更新变量声明。 | 为用作主机变量的所有变量声明添加 `EXEC SQL` 语句。<br />从您的应用程序中删除如下 `EXEC SQL VAR` 声明。<pre>EXEC SQL VAR query IS STRING(2048);</pre> | 应用程序开发人员、应用程序所有者 | 
| 更新 ROWNUM 功能。 | 该 `ROWNUM` 函数在 PostgreSQL 中不可用。将其替换为 SQL 查询中的 `ROW_NUMBER` 窗口函数。<br />Pro\*C 代码：<pre>SELECT SUBSTR(RTRIM(FILE_NAME,'.txt'),12) INTO :gcpclFileseq  <br />FROM   (SELECT FILE_NAME <br />FROM  DEMO_FILES_TABLE <br />WHERE FILE_NAME    LIKE '%POC%' <br />ORDER BY FILE_NAME DESC) FL2 <br />WHERE ROWNUM <=1 ORDER BY ROWNUM;</pre><br />ECPG 代码：<pre>SELECT SUBSTR(RTRIM(FILE_NAME,'.txt'),12) INTO :gcpclFileseq  <br />FROM   (SELECT FILE_NAME , ROW_NUMBER() OVER (ORDER BY FILE_NAME DESC) AS ROWNUM<br />FROM  demo_schema.DEMO_FILES_TABLE <br />WHERE FILE_NAME    LIKE '%POC%'<br />ORDER BY FILE_NAME DESC) FL2 <br />WHERE ROWNUM <=1 ORDER BY ROWNUM; </pre> | 应用程序开发人员、应用程序所有者 | 
| 更新函数参数，以使用别名变量。 | 在 PostgreSQL 中，函数参数不能用作主机变量。使用别名变量覆盖它们。<br />Pro\*C 代码：<pre>int processData(int referenceId){<br />  EXEC SQL char col_val[100];<br />  EXEC SQL select column_name INTO :col_val from table_name where col=:referenceId;<br />}</pre><br />ECPG 代码：<pre>int processData(int referenceIdParam){<br />  EXEC SQL int referenceId = referenceIdParam;<br />  EXEC SQL char col_val[100];<br />  EXEC SQL select column_name INTO :col_val from table_name where col=:referenceId;<br />}</pre> | 应用程序开发人员、应用程序所有者 | 
| 更新结构类型。 | 如果将 `struct` 类型变量用作主机变量，则通过 `typedef` 在 `EXEC SQL BEGIN` 和 `END` 块中定义 `struct` 类型。如果 `struct` 类型是在标头 (`.h`) 文件中定义的，则使用 `EXEC SQL` 包含语句纳入这些文件。<br />Pro\*C 代码：<br />标头文件 (`demo.h`)<pre>struct s_partition_ranges<br />{<br /> char   sc_table_group[31];<br /> char   sc_table_name[31];<br /> char   sc_range_value[10];<br />}; <br />struct s_partition_ranges_ind<br />{<br />  short    ss_table_group;<br />  short    ss_table_name;<br />  short    ss_range_value;<br />}; </pre><br />ECPG 代码：<br />标头文件 (`demo.h`)<pre>EXEC SQL BEGIN DECLARE SECTION;<br />typedef struct <br />{<br />  char   sc_table_group[31];<br />  char   sc_table_name[31];<br />  char   sc_range_value[10];<br />} s_partition_ranges; <br />typedef struct <br />{<br />  short    ss_table_group;<br />  short    ss_table_name;<br />  short    ss_range_value;<br />} s_partition_ranges_ind; <br />EXEC SQL END DECLARE SECTION;</pre><br />Pro\*C 文件 (`demo.pc`)<pre>#include "demo.h"<br />struct s_partition_ranges gc_partition_data[MAX_PART_TABLE] ;<br />struct s_partition_ranges_ind gc_partition_data_ind[MAX_PART_TABLE] ;</pre><br />ECPG 文件 (`demo.pc`)<pre>exec sql include "demo.h"<br />EXEC SQL BEGIN DECLARE SECTION;<br />s_partition_ranges gc_partition_data[MAX_PART_TABLE] ;<br />s_partition_ranges_ind gc_partition_data_ind[MAX_PART_TABLE] ;<br />EXEC SQL END DECLARE SECTION;</pre> | 应用程序开发人员、应用程序所有者 | 
| 修改逻辑以从游标中获取。 | 要使用数组变量从游标中提取多行，请更改代码以使用 `FETCH FORWARD`。<br />Pro\*C 代码：<pre>EXEC SQL char  aPoeFiles[MAX_FILES][FILENAME_LENGTH];<br />EXEC SQL FETCH filename_cursor into :aPoeFiles;</pre><br />ECPG 代码：<pre>EXEC SQL char  aPoeFiles[MAX_FILES][FILENAME_LENGTH];<br />EXEC SQL int fetchSize = MAX_FILES;<br />EXEC SQL FETCH FORWARD :fetchSize filename_cursor into :aPoeFiles;</pre> | 应用程序开发人员、应用程序所有者 | 
| 修改没有返回值的数据包调用。 | 没有返回值的 Oracle 包函数应该使用指示变量来调用。如果您的应用程序包含多个具有相同名称的函数，或者未知类型的函数生成运行时系统错误，请将值强制转换为数据类型。<br />Pro\*C 代码：<pre>void ProcessData (char *data , int id)<br />{        <br />        EXEC SQL EXECUTE<br />               BEGIN<br />                  pkg_demo.process_data (:data, :id);                                                                                    <br />               END;<br />       END-EXEC;<br />}</pre><br />ECPG 代码：<pre>void ProcessData (char *dataParam, int idParam )<br />{<br />        EXEC SQL char *data = dataParam;<br />        EXEC SQL int id = idParam;<br />        EXEC SQL short rowInd;<br />        EXEC SQL short rowInd = 0;<br />        EXEC SQL SELECT pkg_demo.process_data (<br />                       inp_data => :data::text,<br />                       inp_id => :id<br />               ) INTO :rowInd;<br />}</pre> | 应用程序开发人员、应用程序所有者 | 
| 重写 SQL\_CURSOR 变量。 | 重写 `SQL_CURSOR` 变量及其实现。<br />Pro\*C 代码：<pre>/* SQL Cursor */<br />SQL_CURSOR      demo_cursor;<br />EXEC SQL ALLOCATE :demo_cursor;<br />EXEC SQL EXECUTE<br />  BEGIN<br />      pkg_demo.get_cursor(     <br />        demo_cur=>:demo_cursor<br />      );<br />  END;<br />END-EXEC;</pre><br />ECPG 代码：<pre>EXEC SQL DECLARE demo_cursor CURSOR FOR SELECT<br />         * from<br />    pkg_demo.open_filename_rc(<br />            demo_cur=>refcursor<br />          ) ;<br />EXEC SQL char open_filename_rcInd[100]; <br /># As the below function returns cursor_name as <br /># return we need to use char[] type as indicator. <br />EXEC SQL SELECT pkg_demo.get_cursor (<br />        demo_cur=>'demo_cursor'<br />    ) INTO :open_filename_rcInd;</pre> | 应用程序开发人员、应用程序所有者 | 
| 应用常见迁移模式。 | [See the AWS documentation website for more details](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/patterns/migrate-legacy-applications-from-oracle-pro-c-to-ecpg.html) | 应用程序开发人员、应用程序所有者 | 
| 如果需要，启用调试。 | 要在调试模式下运行 ECPG 程序，请在主函数块中添加以下命令。<pre>ECPGdebug(1, stderr); </pre> | 应用程序开发人员、应用程序所有者 | 

### 编译 ECPG 程序
<a name="compile-ecpg-programs"></a>


| Task | 说明 | 所需技能 | 
| --- | --- | --- | 
| 为 ECPG 创建可执行文件。 | 如果您有一个名为 `prog1.pgc` 的嵌入式 SQL C 源文件，则可以使用以下命令序列创建可执行程序。<pre>ecpg prog1.pgc<br />cc -I/usr/local/pgsql/include -c prog1.c<br />cc -o prog1 prog1.o -L/usr/local/pgsql/lib -lecpg</pre> | 应用程序开发人员、应用程序所有者 | 
| 创建一个 make 文件用于编译。 | 创建一个 make 文件以编译 ECPG 程序，如以下示例文件中所示。<pre>CFLAGS ::= $(CFLAGS) -I/usr/pgsql-12/include -g -Wall<br />LDFLAGS ::= $(LDFLAGS) -L/usr/pgsql-12/lib -Wl,-rpath,/usr/pgsql-12/lib<br />LDLIBS ::= $(LDLIBS) -lecpg<br />PROGRAMS = test <br />.PHONY: all clean<br />%.c: %.pgc<br />      ecpg $<<br />all: $(PROGRAMS)<br />clean:<br />    rm -f $(PROGRAMS) $(PROGRAMS:%=%.c) $(PROGRAMS:%=%.o)</pre> | 应用程序开发人员、应用程序所有者 | 

### 测试应用程序
<a name="test-the-application"></a>


| Task | 说明 | 所需技能 | 
| --- | --- | --- | 
| 测试代码。 | 测试转换后的应用程序代码，以确保其正常运行。 | 应用程序开发人员、应用程序所有者、测试工程师 | 

## 相关资源
<a name="migrate-legacy-applications-from-oracle-pro-c-to-ecpg-resources"></a>
+ [ECPG-C 语言中的嵌入式 SQL](https://www.postgresql.org/docs/current/static/ecpg.html)（PostgreSQL 文档）
+ [错误处理](https://www.postgresql.org/docs/12/ecpg-errors.html)（PostgreSQL 文档）
+ [为什么要使用 Oracle Pro\*C/C\+\+ 预编译器](https://docs.oracle.com/cd/E11882_01/appdev.112/e10825/pc_01int.htm#i2415)（Oracle 文档）

## 附加信息
<a name="migrate-legacy-applications-from-oracle-pro-c-to-ecpg-additional"></a>

PostgreSQL 有一个嵌入式 SQL 预编译器 ECPG，相当于 Oracle Pro\*C 预编译器。ECPG 通过用特殊函数调用替换 SQL 调用，将嵌入 SQL 语句的 C 程序转换为标准 C 代码。然后可以使用任何 C 编译器工具链处理输出文件。

**输入和输出文件**

ECPG 将您在命令行上指定的每个输入文件转换为相应的 C 输出文件。如果输入文件名没有文件扩展名，则假定为 .pgc。将文件扩展名替换为 `.c`，以构造输出文件名。但是，可以使用 `-o` 选项覆盖默认输出文件名。

如果您使用破折号 (`-`) 作为输入文件名，ECPG 会从标准输入中读取程序并写入标准输出，除非您使用该 `-o` 选项将其覆盖。

**标头文件**

当 PostgreSQL 编译器编译预处理的 C 代码文件时，它会在 PostgreSQL `include` 目录中查找 ECPG 标头文件。因此，您可能必须使用 `-I` 选项将编译器指向正确的目录（例如 `-I/usr/local/pgsql/include`）。

**库**

使用 C 代码和嵌入式 SQL 的程序必须链接到 `libecpg` 库。例如，您可使用链接器选项 ` -L/usr/local/pgsql/lib -lecpg`。

转换后的 ECPG 应用程序通过嵌入式 SQL `libpq` 库 (`ecpglib`) 调用库中的函数，并使用标准协议与 PostgreSQL 服务器通信。 frontend/backend 