如今,全世界 IBM i 客戶都在 使用 SQL 定義和訪問數據。IBM 發布的每個 IBM i 新版本都繼續提供豐富的新函數和功能,但它們只能通過 SQL 使用 。DB2 Modernization 白皮書列示并描述了這些增強功能。 這些基于 SQL 的新應用程序正在利用現代數據中心開發概
如今,全世界 IBM i 客戶都在使用 SQL 定義和訪問數據。IBM 發布的每個 IBM i 新版本都繼續提供豐富的新函數和功能,但它們只能通過 SQL 使用。DB2 Modernization 白皮書 列示并描述了這些增強功能。
這些基于 SQL 的新應用程序正在利用現代數據中心開發概念,某些概念強調將更多業務規則和邏輯推到數據庫中的重要性。
許多客戶仍然依賴以 IBM 的 RPG 編程語言編寫的傳統應用程序。一些 PRG 程序也能從利用現代數據中心技術中受益。這些現有 RPG 應用程序與新的 SQL 應用程序包含相同的業務規則,但它們是許多年前開發的,所使用的“一次一條記錄” 的傳統數據訪問方法比較低效。重復的規則和邏輯會(其實已經)導致昂貴的雙重維護和多個事實版本的存在。
Rational Open Access: RPG Edition(或 ROA)支持(自 IBM i 7.1 中引入且回溯支持 6.1)攔截 RPG IO 操作并將其轉換為 SQL,從而消除了雙重維護的巨大成本和 / 或昂貴的程序重寫?,F在可以將一個新關鍵字 (HANDLER) 添加到現有 PRG 文件規范中。HANDLER 關鍵字現在可以指定 IBM i 操作系統調用的程序或服務程序入口點。這個新支持能夠極大地減少重寫歷史遺留代碼的工作量,如果沒有它,則需要轉換現有程序來共享公共模塊。清單 1 展示了一個 HANDLER 編碼示例。
FEMPADDRSL1UF E K DISK //-------------------Rational Open Access Addition ---------------------- // The handler keyword specifies the program/service program which will // now process the IO operations for this file F handler('UPDHANDLER')
在本文中,我將描述如何創建一個基于格式的 HANDLER,支持多個程序使用一個公共格式和唯一鍵更新同一個表,以便利用單個通用 SQL UPDATE 語句,該語句利用擴展指示符變量支持。
我將使用的方法如下所示:
我選擇了 RPG IV 自由格式樣式作為我的基于主機的編程語言。這個選擇的主要原因是我相信使用 Rational Open Access: RPG Edition 對 RPG 程序員的吸引力更大。但是,重要的是要注意,Handler 程序和 和 SQL 程序可以使用 IBM i 上支持的任何基于主機的語言編寫。
我在代碼示例中也進行了一些變通:合并過程,移動和刪除代碼,等等。這可能會導致代碼示例不能按原樣編譯。另外,有些代碼只有在安裝 Rational Open Access 產品后才能工作。
構建一個基于記錄格式的 Handler 程序
可以通過兩種方法使用 Rational Open Access 在 RPG 程序和 Handlder 程序之間傳遞數據。第一種方法是基于結構的,其中數據和鍵值通過緩沖區傳遞。這些緩沖區可以是程序描述的,也可以是外部描述的。第二種方法是基于列的,其中數據和鍵值作為獨立項目傳遞。每個項目都包含數據或鍵字段的屬性。采用的方法與需要編寫的 Handler 程序的數量有直接關系。無論采用哪種方法,都需要使用動態嵌入式 SQL 來最小化編碼量和 Handler 程序的總數。
動態 SQL 是創建通用數據庫 Handler 程序的理想選擇??梢圆渴鹑N類型的通用數據庫 Handler 程序,您可以根據您的 ILE 和 SQL 專業技術水平進行選擇。這三種類型是基于記錄格式、語句和過程的 Handler 程序?;诟袷降?Handler 程序非常適合固定列表樣式的動態 SQL?;谡Z句的 Handler 程序更適合變動列表樣式的動態 SQL?;谡Z句的 Handler 程序最適合使用結果集或 MERGE 之類的 “一氣呵成”型 SQL 語句。
固定列表動態 SQL 通常用于以下情況:應用程序提供動態更改行選擇(通過 WHERE 子句)或列排序(通過 ORDER BY 子句)的功能,盡管計算出的結果集是靜態的。在 Handler 應用程序中,固定列表動態 SQL 將用于基于結構的方法。使用固定列表動態 SQL 時不需要 SQL 描述符。
當應用程序從頭開始構建整個 SQL 語句時,通常會用到變動列表動態 SQL。在這種情況下,需要使用 SQL 描述符來描述 SQL 語句、結果集中計算出的列以及任何輸入參數。有兩種描述符類型:一種通過 SQL ALLOCATE DESCRIPTOR 創建,另一種基于 SQLDA 結構定義。要詳細了解動態 SQL,請在線參閱 SQL 編程指南:http://ibm.com/systems/i/db2/books.html。
對于本文,我將結合使用固定列表動態 SQL 和靜態嵌入式 SQL(用于 UPDATE 語句)來創建一個基于格式的 Handlder 程序。在將來的文章中,我將介紹如何創建基于語句和基于過程的 Handlder 程序。
使用 ROA 產品的一個主要好處是無需費時費力地修改和測試現有程序。對于需要維護結構不良的現有 RPG 代碼或利用編程模型提供的模塊功能的開發人員來說,這種分離 IO 的方法可能是一個 “全新的起點”。
圖 1 簡要展示了 Handlder 程序如何提供一個通往一個依賴 SQL 進行數據訪問的現有過程集的橋梁。這些過程可以是使用嵌入式 SQL 編寫的外部存儲過程,也可以完全以 SQL PL 編寫。在本文中,我將用到使用嵌入式 SQL 的 RPG 子過程。
Handler 使用 ILE 向 RPG 程序提供一個原型化接口。要使用 Handler 程序,現有 RPG 程序必須是一個 ILE 程序(也稱為 RPG IV)。因此,如果現有 RPG 程序不是 ILE,那么首先需要將 RPG 程序轉換為 ILE。為此,要用到 Convert RPG Source (CVTRPGSRC) 命令。
我們的 Handlder 程序包含一個主模塊,它將 RPG 操作導向一個對應的 Handlder 程序模塊。這允許按照應用程序類型定制主模塊。例如,針對只有輸入的文件的 Handlder 程序不需要更新模塊。
各個 Handler 程序模塊包含將 RPG IO 操作轉換為適當的 SQL 操作的邏輯。轉換代碼存儲在子過程中。另外,Handler 程序模塊包含一些原型化調用,它們調用一個包含嵌入式 SQL 子過程的 RPG SQL 服務程序。目前,可能有 19 個數據庫相關 RPG IO 操作最終需要通過我們的 Handler 程序處理,我使用其中 4 個:OPEN、CHAIN、UPDATE 和 CLOSE。
圖 2 包含描繪上述場景的 Rational Visualize Application Diagram。Handlder 程序主過程 UPDHANDLER 將入站 RPG IO 操作提交到適當的子過程。子過程在調用對應的 RPG SQL 服務程序子過程之前執行轉換邏輯。
圖 2 的放大圖
Handlder 程序主過程 (UPDHANDLER)
基于格式的 Handler 程序需要定義要處理的文件的記錄格式,為此,需要在 Handler 程序模塊中編碼用于文件定義、文件記錄格式、文件密匙和 SQL 空指示符數組的模板,如 清單 2 所示。這些模板被編碼為 RPG 全局變量,以便允許將來重用通用模板名稱并最小化代碼修改。
FrcdFile_t IF E K DISK TEMPLATE F EXTDESC('EMPADDRESS') F RENAME(empAddr:rcdFormat) D rcdFormat_t... D DS LIKEREC(rcdFormat) D TEMPLATE D keys_t DS LIKEREC(rcdFormat:*KEY) D TEMPLATE D Ind_Array_t... D S 5i 0 DIM(7) D TEMPLATE
清單 2 包含一個文件模板定義代碼示例。IBM i 6.1 中新引入的 TEMPLATE 關鍵字通知 RPG 編譯器:這個文件僅用于字段定義,不需要、也不允許為該文件進行 IO 操作。EXTDESC 關鍵字不需要。在本例中,它用于定義包含 Handlder 程序中使用的記錄格式定義的真實文件。
RENAME 關鍵字用于提供一個通用格式名稱,以便減少代碼更改,如果這個代碼用作其他格式 Handlder 程序的模板的話。rcdFormat_t 數據結構模板展示了這一點,該模板基于重命名的格式。這個數據結構模板用于 Handlder 程序中的其他模塊中。
SQL 空指示符數組用于 RPG HANDLE_CHAIN 和 RPG HANDLE_UPDATE 子過程中。空指示符值在 SQL FETCH 語句執行過程中填充。空指示符值在執行 SQL UPDATE 語句之前通過 RPG HANDLE_UPDATE 子過程設置。
為一個文件指定 HANDLER 關鍵字之后,針對該文件執行的所有顯式或隱式 IO 操作都將通過 Handlder 程序處理。程序員的職責是編碼必要的指令來處理 IO 操作并提供適當的結果。ROA 產品隨 RPG include 文件 (QRNOPENACC) 一起提供,該文件包含傳遞到 Handlder 程序的參數的子字段定義。
清單 3 包含 UPHANDLER 程序中的主過程的示例。輸入參數 (rpgIO) 是一個數據結構,其定義方式類似于 QrnOpenAccess_t 模板,該模板是 QRNOPENACC include 模塊的一部分。主過程在以下子字段上操作:
D UPDHANDLER PI D rpgIO... D LIKEDS(QrnOpenAccess_T) /COPY QOAR/QRPGLESRC,QRNOPENACC /FREE rpgIO.rpgStatus = *Zero; select; when rpgIO.rpgOperation = QrnOperation_OPEN; rpgIO.rpgStatus = Handle_Open(rpgIO); when rpgIO.rpgOperation = QrnOperation_CHAIN; rpgIO.rpgStatus = Handle_Chain(rpgIO); when rpgIO.rpgOperation = QrnOperation_UPDATE; rpgIO.rpgStatus = Handle_Update(rpgIO); when rpgIO.rpgOperation = QrnOperation_CLOSE; rpgIO.rpgStatus = Handle_Close (rpgIO); Other; ENDSL; //If unrecoverable error then shutdown handler If rpgIO.rpgStatus = 1299; *INLR = *On; ENDIF; return; /END-FREE
子過程接口
每個 RPG IO 操作子過程都使用一個公共接口,該接口包含 rpgIO 參數和一個與 rpgStatus 碼對應的返回字段。另外,每個過程都在輸入時將 rpgStatus 設置為 0 并使用 RPG Monitor 功能來處理意外錯誤。這樣的錯誤出現時,rpgStatus 字段被設置為 1299(檢測到其他 I/O 錯誤)。這將導致正被處理的 RPG 程序內部出現一個異常。
清單 4 包含這個公共 Handlder 程序子過程接口的一個代碼段。
P Handle_Open B D Handle_Open PI LIKE(rpgIO.rpgStatus) D rpgIO... D LIKEDS(QrnOpenAccess_T) D* Local fields D retField S LIKE(rpgIO.rpgStatus) /FREE retField = *Zero; Monitor; //routine specific data and code begins here On-Error; retField = 1299; ENDMON; return retField; /END-FREE P Handle_Open E
處理隱式和顯式 RPG 打開操作
RPG 文件打開可以在 RPG 初始化階段隱式發生,也可以通過 RPG OPEN 操作或在文件定義上使用 USROPN 關鍵字顯式發生。在多數情況下,隱式打開就足夠了。顯式 OPEN 操作的使用在 RPG 程序和 Handlder 程序之間提供更多交互。
應該在 Handlder 程序 OPEN 例程中完成的第一件事是設置 IO Information Feedback 數據結構的指針和長度。這些是意外錯誤出現時可以在 RPG 程序中使用的結構。它允許您將額外的狀態信息返回 RPG 程序。清單 5 包含設置 rpgIO 數據結構中的這些值的代碼段。
rpgIO.openFeedback = %Alloc(80);
rpgIO.ioFeedback = %Alloc(160);
rpgIO.deviceFeedback = %Alloc(126);
rpgIO.openFeedbackLen = 80;
rpgIO.ioFeedbackLen = 160;
rpgIO.deviceFeedbackLen = 126;
下一個重要項目是 rpgIO stateInfo 指針。這個可選指針用于分配臨時存儲空間,以便存儲對 Handlder 程序的調用和來自 Handlder 程序的調用之間需要保留的信息。這種項目的一個例子是記錄前映像。Handlder 程序將比較記錄前映像和從 RPG 更新操作傳遞到 Handlder 程序中的值。我們將在 Handle-Update 子過程討論中進一步討論這一點。
清單 6 包含的代碼示例展示了如何聲明一個狀態信息結構,然后根據該結構的大小向 stateInfo 指針分配存儲空間。
//stateinfo data structure template
D rpgSI_t...
D DS TEMPLATE
D OLD_ROW_p *
/FREE
rpgIO.stateInfo = %Alloc(%Size(rpgSI_t));
現在,我們已經準備好構造將用于返回輸入記錄、以響應 RPG CHAIN 操作的 SQL 語句了。這個語句將是一個傳遞到 Prepare_SQL_Statement 子過程的字符串變量。圖 3 詳細展示了處理一個 RPG 隱式文件打開操作的過程和主要函數的代碼片段。RPG 程序打開的文件的名稱被傳遞到 rpgIO externalFile 結構中的 Handlder 程序。這個結構包含兩個子字段:庫和名稱。如果庫列包含 *LIBL,那么只有外部文件名稱被用于表引用,否則,庫和名稱列將被合并以形成一個被限定的表引用。然后,表引用變量被用于 SQL Select 語句 FROM 子句。對 RPG PREPARE_SQL_STATEMENT 子過程的調用失敗時,將向正被處理的 RPG 程序返回 rpgStatus 值 1299。圖 3 中的 HANDLE_OPEN 流程圖標下方的代碼段展示了這一點。
一個 WHERE 子句被添加到一個 Select 語句字符串,該字符串表示 RPG CHAIN 操作中使用的鍵字段對應的一列或多列。在本例中,EMPNO 是表的唯一鍵。此時,EMPNO 的值是未知的,因此一個參數標記 (?) 被用作占位符。實際值將在第一個 RPG 鍵 IO 操作發生時提供。一個 FOR FETCH ONLY 子句被添加到 Select 語句,以避免記錄鎖定并利用 SQL 自動塊操作的優勢。
SQL 語句一旦完成格式化,將被傳遞到 RPG PREPARE_SQL_STATEMENT 子過程。包含嵌入式 SQL 語句的 RPG 過程通常被創建為獨立模塊,然后用于創建服務程序。服務程序可以在編譯時被綁定到 Handlder 程序。SQL 模塊也必須包含格式模板代碼,如 清單 2 所示。另外,要支持 SQL 擴展指示符的使用,SQL 模塊要么需要使用預編譯器命令的 OPTION 參數上的 *EXTIND 進行編譯,要么包含一個指定 EXTIND(*YES) 的 SQL Set Option 語句。Set Option 語句需要在模塊中的其他 SQL 語句之前指定。
圖 3 包含 RPG PREPARE_SQL_STATEMENT 子過程的一個 RPG 代碼示例,該子過程準備一個動態 SQL 語句字符串 (v_SQL_String),如果成功,就為準備好的 SQL 語句聲明一個 SQL 游標。SQL 語句字符串 (p_SQL_String) 從 RPG HANDLE_OPEN 子過程傳遞到 RPG 子過程,以響應 RPG 打開操作。
處理 RPG CHAIN 操作
RPG 程序更新通過鍵訪問的行之前,必須執行一個讀操作,以便鎖定該行,進行更新。使用鍵執行隨機讀操作的最常見 RPG 方法是 CHAIN 操作。圖 4 詳細展示了處理一個 RPG CHAIN 操作的過程以及主要函數的代碼段。
使用基于結構的 IO 方法時,RPG 將為輸入和輸出緩沖區創建初始存儲空間,并為空指示符映射創建存儲空間。rpgIO 參數包含指向這些存儲空間區域的指針。RPG HANDLE_CHAIN 子過程定義類似于此前定義的全局模板的數據結構。數據結構中包含的數據基于 RPG 提供的指針。清單 7 提供數據結構定義的源代碼示例。圖 4 包含 RPG HANDLE_CHAIN 子過程的 RPG 源代碼示例。
D Handle_Chain PI LIKE(rpgIO.rpgStatus)
D rpgIO LIKEDS(QrnOpenAccess_T)
D i S 5i 0
D keys DS LIKEDS(keys_t)
D BASED(rpgIO.key)
D inpRcd DS LIKEDS(rcdFormat_t)
D BASED(rpgIO.inputBuffer)
D OLD_ROW DS LIKEDS(rcdFormat_t)
D BASED(rpgSI.OLD_ROW_p)
D rpgSI...
D DS LIKEDS(rpgSI_t)
D BASED(rpgIO.stateInfo)
D IndAry S 5i 0 DIM(%elem(Ind_Array_t))
D InpNullMap S N DIM(%elem(Ind_Array_t))
D BASED(rpgIO.inputNullMap)
一旦 RPG 程序接收到數據,inputBuffer 指針將被 RPG 設置為空。為了比較記錄前后映像,Handlder 程序必須創建一個輸入記錄副本。原來的行的存儲空間被定義為 清單 6 中描述的 stateInfo 數據結構的一部分。這確保數據在 CHAIN 和后續 UPDATE 操作之間保留。
如果列被定義為支持空值,那么您必須相應更新空指示符映射。RPG 使用一個 1 字符字段(比如一個指示符)來確定列是否包含空值。如果列不包含空值,則 SQL 使用一個包含一個 0 的小整數;反之則包含 -1。
在 SQL 中,沒有與 RPG CHAIN 對等的函數,因此我選擇使用 FETCH FIRST 語句和一個游標作為適當的替代選項。這也解釋了使用 PREPARE、DECLARE、OPEN 和 CLOSE SQL 游標語句的原因。
另一種思路的人可能建議使用 SELECT INTO 作為 CHAIN 操作的替代選項。它在某種程度上類似于使用 CHAIN 的 RPG 程序,因為它隱式執行準備,打開并關閉函數;但是它的缺點大大超過了這個快捷功能。這些缺點是:
圖 4 包含 FETCH_FIRST_FROM_OPEN_CURSOR 子過程的一個 RPG 源代碼樣例。RPG HANDLE_CHAIN 子過程傳遞這個鍵(或多個鍵)、inputBuffer 指針和指示符數組。
這個鍵(或多個鍵)用于替代這個參數標記(或多個標記),參數標記被定義為在 RPG HANDLE_OPEN 子過程中創建的 SQL 語句字符串的一部分。OPEN CURSOR 語句作為 FETCH 過程的一部分執行。如果 OPEN 成功,則 SQL FETCH FIRST 語句被執行。inpRcd 結構和 indAry 參數包含成功 FETCH 的結果。SQL OPEN 和 FETCH 的結合等同于代表 RPG CHAIN 執行的系統指令。本質上,OPEN 定位到索引中,FETCH FIRST 將基于索引提供的 RRN 檢索第一行。
在同一個會話或作業中第二次執行 OPEN 后,SQL 游標將變得可重用。這意味著后續 SQL OPEN 和 CLOSE 操作只是將游標重新定位到結果集的第一行。無論 RPG 程序是否正在使用 LR 指示符,這種行為都會發生。這允許將 LR 持續用作一個內務整理工具,同時避免了 SQL 打開和關閉開銷的高昂成本。
處理 RPG UPDATE 操作
要理解 RPG HANDLE_UPDATE 子過程,我們需要討論擴展指示符變量支持的概念。在 DB2 for i 6.1 發布之前,SQL Update 和 Insert 語句能夠使用一個值為 -1 的指示符變量,將支持空值的列設置為空值。使用 6.1 版中引入的擴展指示符變量支持,可以通過提供額外的指示符值來擴展更新和插入功能。
最有趣的一個功能是使用指示符值 -7 來允許更新繞過一列,就好像該列不是 UPDATE 語句的一部分。這個支持允許您編寫一個可用于任何更新事務的通用更新過程,而不管正在更新哪些列。圖 5 詳細展示了處理一個 RPG UPDATE 操作的過程和主要函數的代碼段。
圖 5 中的來自 RPG HANDLE_UPDATE 子過程的代碼段顯示,更新指示符數組被初始化為 -7。outputBuffer 中的值與從 inputBuffer 保存的值相比較。對于每個不同的值,該列的指示符變量被設置為 0??梢蕴砑宇~外的代碼來執行一個檢查,檢查新值是不是一個用戶定義值,用以表明數據庫值應該被設置為 NULL 或針對該列定義的默認值。如果是前一種情況,那么該列的指示符變量被設置為 -1,這導致該列被更新為空值。如果是后一種情況,那么該列的指示符變量被設置為 -5,這將把該列的值設置為默認值。如果新舊值相同,那么指示符變量仍然是 -7,該列被忽略。RPG HANDLE_UPDATE 子過程是特定于格式的,必須針對每個文件進行定制。
比較完所有列之后,將執行 RPG UPDATE_COLUMNS_USING_EXTENDED_INDICATORS 子過程。RPGUPDATE_COLUMNS_USING_EXTENDED_INDICATORS 子過程接受兩個參數:updRcd 和 Ind_Ary。updRcd 參數包含表中的可更新列的值(已更改的值和未更改的值)。Ind_Ary 列包含 SQL 擴展指示符設置。
圖 5 包含 RPG UPDATE_COLUMNS_USING_EXTENDED_INDICATORS 子過程的一個 RPG 源代碼樣例。UPDATE 上使用的表名是包含清單 2 中使用的格式的文件的名稱。在 IBM Data Access Reengineering 策略中,這個文件稱為代理文件。要詳細了解如何現代化數據訪問,請訪問 DB2 for i 網站。
指示符變量不能通過數組索引技術指定。每個指示符變量都必須單獨命名。為繞過這個限制,我使用一個數據結構來為每個數組元素定義一個已命名指示符變量。這個數據結構基于 Ind_Ary 的地址。清單 8 包含將輸入指示符數組重新定義到一個數據結構的 RPG 源代碼示例。
D Update_Columns_Using_Extended_Indicators... D PI N D updRcd... D LIKEDS(rcdFormat_t) D Ind_Ary... D LIKE(Ind_Array_t) D DIM(%elem(Ind_Array_t)) D* Local fields D retField S N D Ind_Ary_DS DS BASED(Ind_Ary_Ptr) D Ind_Ary_1 5i 0 D Ind_Ary_2 5i 0 D Ind_Ary_3 5i 0 D Ind_Ary_4 5i 0 D Ind_Ary_5 5i 0 D Ind_Ary_6 5i 0 D Ind_Ary_7 5i 0 /FREE Ind_Ary_Ptr = %Addr(Ind_Ary);
處理隱式和顯式 RPG 關閉操作
Handlder 程序必須執行兩個函數:一是關閉 SQL 游標;二是取消 stateInfo 數據結構使用的內存的分配。圖 6 詳細展示了處理一個隱式或顯式 RPG CLOSE 操作的過程和主要函數的代碼段。
RPG 程序打開 Last Record (*INLR) 指示符,這導致針對已處理文件的一個隱式 CLOSE 操作。Handlder 程序攔截 RPG CLOSE 操作并將控制權傳遞給 RPG HANDLE_CLOSE 子過程。分配給 stateInfo 指針的內存被取消。一個調用被發送到 RPG CLOSE_SQL_CURSOR 子過程,該子過程包含嵌入的 SQL CLOSE 語句。
實現場景
下列場景用于測試 Handlder 程序代碼:(1)使用一個 5250 顯示文件的傳統 RPG 程序;(2)從一個 Java 應用程序調用的外部存儲過程。5250 場景在許多傳統 RPG 商店中很常見,在這類商店中,有許多程序正在訪問同一個表以進行更新。Java 場景描述的技術采用單個外部存儲過程進行表更新,而不是在很多應用程序中包含多個 SQL UPDATE 語句。其目標是對同一個表的所有更新擁有單點控制,而不管更新的起源點。這些場景如 圖 7 所示。
本質上,單個基于格式的 Handlder 程序能夠服務于多個使用各種 UPDATE 方法的基于主機的 RPG 5250 程序。這包括使用數據結構或 RPG 內置函數 %FIELDS 更新文件或記錄格式。另外,一旦一個 RPG 程序被注冊為一個外部存儲過程,任何支持存儲過程調用的接口(Java、PHP,等等)都能訪問這個 RPG 程序。這種外部存儲過程方法還允許瀏覽器客戶端等外部應用程序利用 DB2 for i 擴展指示符支持。
清單 9 包含一個用于將一個 RPG 程序注冊為一個外部存儲過程的 SQL CREATE PROCEDURE 語句的代碼示例。
CREATE PROCEDURE TEST_UPDFIELDS ( IN p_ADDRESS1 VARCHAR(30) ,IN p_ADDRESS2 VARCHAR(30) ,IN p_ADDRESS3 VARCHAR(30) ,IN p_CITY VARCHAR(15) ,IN p_STATE VARCHAR(10) ,IN p_ZIPCODE VARCHAR(10) ,IN Unique_Key CHAR(6) ) LANGUAGE RPGLE PARAMETER STYLE GENERAL WITH NULLS RESULT SETS 2 NOT DETERMINISTIC MODIFIES SQL DATA EXTERNAL NAME RPGEXTPROC
清單 10 包含已經注冊為外部存儲過程的現有 RPG 程序的代碼。UPHANDLER 程序將處理的 RPG IO 操作碼是 BOLD。這個 RPG 程序正在定義代理文件以便進行更新。清單 9 中顯示的來自 SQL CREATE PROCEDURE 語句的 PARAMETER STYLE GENERAL WITH NULLS 子句指定,一個空指示符數組將作為一個額外參數被發送給外部存儲過程。對于傳遞到外部存儲過程的每個輸入參數,數組中都將有一個元素。RPG 程序使用這個參數 (p_Ind_Ary) 來確定輸入參數是否包含一個不等于 NULL (-1) 的值。如果是,那么使用輸入參數值更改相應的數據列。由于文件 EMPADDRESS 正在被程序 UPDHANDLER 處理,因此 UPDATE 操作按照此前 圖 5 中描述的方法處理。
FEMPADDRESSUF E K DISK F handler('UPDHANDLER') D i S 5i 0 D HandleTheInternet... D PR EXTPGM('RPGEXTPROC') D p_ADDRLINE1 LIKE(ADDRLINE1) D p_ADDRLINE2 LIKE(ADDRLINE2) D p_ADDRLINE3 LIKE(ADDRLINE3) D p_CITY LIKE(CITY) D p_STATE LIKE(STATE) D p_ZIPCODE LIKE(ZIPCODE) D p_EMPNO LIKE(EMPNO) D p_Ind_Ary... D 5i 0 DIM(7) P HandleTheInternet... P B D HandleTheInternet... D PI D p_ADDRLINE1 LIKE(ADDRLINE1) D p_ADDRLINE2 LIKE(ADDRLINE2) D p_ADDRLINE3 LIKE(ADDRLINE3) D p_CITY LIKE(CITY) D p_STATE LIKE(STATE) D p_ZIPCODE LIKE(ZIPCODE) D p_EMPNO LIKE(EMPNO) D p_Ind_Ary... D 5i 0 DIM(7) D inpRecord Ds LIKEREC(EMPADDR:*INPUT) /Free Monitor; CHAIN(e) p_EMPNO EMPADDRESS inpRecord; If %Found; If p_Ind_Ary(1) = *Zero; inpRecord.ADDRLINE1 = p_ADDRLINE1; ENDIF; If p_Ind_Ary(2) = *Zero; inpRecord.ADDRLINE2 = p_ADDRLINE2; ENDIF; If p_Ind_Ary(3) = *Zero; inpRecord.ADDRLINE3 = p_ADDRLINE3; ENDIF; If p_Ind_Ary(4) = *Zero; inpRecord.CITY = p_CITY; ENDIF; If p_Ind_Ary(5) = *Zero; inpRecord.STATE = p_STATE; ENDIF; If p_Ind_Ary(6) = *Zero; inpRecord.ZIPCODE = p_ZIPCODE; ENDIF; inpRecord.EMPNO = p_EMPNO; Update(E) EMPADDR inpRecord; ENDIF; On-Error; ENDMON; *INLR = *On; //Implicit CLOSE Return; /End-Free P HandleTheInternet... P E
結束語
在本文中,我向您介紹了如何使用 Rational Open Access: RPG Edition 轉換傳統的 “一次一條記錄” 訪問方法,以便利用擴展指示符變量支持之類的高級 SQL 技術,這只需在現有 RPG 程序中更改一行代碼。
使用擴展指示符支持,單個程序可以基于一個公共鍵用于單個表的所有更新事務。這包括使用傳統更新方法的現有程序和能夠利用存儲過程調用的外部接口。使用這種技術,更新一個表中的一個列子集無需構建多個 SQL 語句。
我還介紹了基于格式的 Handlder 程序的概念,這種 Handlder 程序允許您將 RPG 輸入和輸出緩沖區用作在 RPG 程序和 Handlder 程序之間移動數據的機制?;诟袷降?Handlder 程序利用 RPG IV 的新模板功能。它還構建在外部描述的數據結構的概念之上,這是固定列表動態 SQL 使用的主機變量的一種軟編碼方法。
在未來的文章中,我將擴展這些概念,創建一個基于語句的 Handlder 程序,與變化列表動態 SQL 聯合使用,這可以極大地減少需要的 Handlder 程序的數量。我將使用這些技術來利用以下高級 SQL 功能:
通過 IBM Rational Open Access: RPG Edition 產品,只需對現有程序進行很小的更改即可利用上述所有功能。
本文來自IBM developerWorks,地址:
http://www.ibm.com/developerworks/cn/ibmi/library/i-roaforsql/index.html
聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com