

本文為英文版的機器翻譯版本，如內容有任何歧義或不一致之處，概以英文版為準。

# 將舊版應用程式從 Oracle Pro\*C 遷移至 ECPG
<a name="migrate-legacy-applications-from-oracle-pro-c-to-ecpg"></a>

*Sai Parthasaradhi 和 Mahesh Balumuri，Amazon Web Services*

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

大多數具有內嵌 SQL 程式碼的舊版應用程式會使用 Oracle Pro\*C 前置編譯器來存取資料庫。當您將這些 Oracle 資料庫遷移至 PostgreSQL 的 Amazon Relational Database Service (Amazon RDS) 或 Amazon Aurora PostgreSQL 相容版本時，您必須將應用程式程式碼轉換為與 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>

**先決條件**
+ 作用中的 AWS 帳戶
+ Amazon RDS for PostgreSQL 或 Aurora PostgreSQL 相容資料庫
+ 在內部部署執行的 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 Command Line Interface (AWS CLI) 是一種開放原始碼工具，可透過命令列 shell 中的命令與 AWS 服務互動。透過最少的組態，您可以從命令提示中執行 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>


| 任務 | Description | 所需的技能 | 
| --- | --- | --- | 
| 安裝 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 工程師 | 
| 安裝 AWS CLI。 | 安裝 AWS CLI 來執行命令，以從您的應用程式與 AWS Secrets Manager 和 Amazon Simple Storage Service (Amazon S3) 等 AWS 服務互動。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>


| 任務 | Description | 所需的技能 | 
| --- | --- | --- | 
| 移除不需要的標頭。 | 移除 PostgreSQL 中不需要的`include `標頭，例如 `oci.h`、 `oratypes`和 `sqlda`。 | 應用程式擁有者、應用程式開發人員 | 
| 更新變數宣告。 | 為用作主機變數的所有變數宣告新增`EXEC SQL`陳述式。<br />從您的應用程式中移除宣告，`EXEC SQL VAR`例如以下內容。<pre>EXEC SQL VAR query IS STRING(2048);</pre> | 應用程式開發人員、應用程式擁有者 | 
| 更新 ROWNUM 功能。 | PostgreSQL 中無法使用 `ROWNUM`函數。將此取代為 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> | 應用程式開發人員、應用程式擁有者 | 
| 更新結構類型。 | `typedef` 如果`struct`類型變數用作主機變數，請使用 定義 `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_tw/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>


| 任務 | Description | 所需的技能 | 
| --- | --- | --- | 
| 建立 ECPG 的可執行檔。 | 如果您有名為 的內嵌 SQL C 來源檔案`prog1.pgc`，您可以使用下列命令序列來建立可執行程式。<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>


| 任務 | Description | 所需的技能 | 
| --- | --- | --- | 
| 測試代碼。 | 測試轉換後的應用程式程式碼，以確保其正常運作。 | 應用程式開發人員、應用程式擁有者、測試工程師 | 

## 相關資源
<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 陳述式的 C 程式轉換為標準 C 程式碼，方法是將 SQL 呼叫取代為特殊函數呼叫。然後，可以使用任何 C 編譯器工具鏈處理輸出檔案。

**輸入和輸出檔案**

ECPG 會將您在命令列指定的每個輸入檔案轉換為對應的 C 輸出檔案。如果輸入檔案名稱沒有副檔名，則會假設 .pgc。檔案的副檔名由 取代`.c`，以建構輸出檔案名稱。不過，您可以使用 `-o`選項覆寫預設輸出檔案名稱。

如果您使用破折號 (`-`) 做為輸入檔案名稱， ECPG 會從標準輸入 讀取程式，並寫入標準輸出，除非您使用 `-o`選項覆寫該程式。

**標頭檔案**

當 PostgreSQL 編譯器編譯預先處理的 C 程式碼檔案時，它會在 PostgreSQL `include`目錄中尋找 ECPG 標頭檔案。因此，您可能必須使用 `-I`選項，將編譯器指向正確的目錄 （例如，`-I/usr/local/pgsql/include`)。

**Libraries (程式庫)**

使用 C 程式碼搭配內嵌 SQL `libecpg`的程式必須連結到程式庫。例如，您可以使用連結器選項` -L/usr/local/pgsql/lib -lecpg`。

轉換後的 ECPG 應用程式會透過內嵌 SQL 程式庫 (`ecpglib`) 呼叫`libpq`程式庫中的函數，並使用標準前端/後端通訊協定與 PostgreSQL 伺服器通訊。