メニューボタン
IBMi海外記事2020.02.25

さらにモダンなRPG言語

Bob Cozzi 著

1988年に、私はRPG IIIに関する本を出版しました。その後、1996年にはRPG IV版を刊行し、2000年頃にはもう一度改訂を行いました。しかし、その後の数年間は、RPG IVは勢いがほとんど失われていたようでした。あちらこちらに微調整がなされましたが、目を見張るようなものはなかったようです。

ここ数年は、様々なRPG IVの機能強化が相次いで公開されましたが、中でも注目すべきは、フリーフォーマットが完成したことであり、RPG IVを再び真のモダンな言語に押し上げる一助となりました。RPG IVにとっての「モダン」の尺度は、フリーフォーマット構文がどれくらい多くサポートされるかという点に向かいがちのようですが、COBOLおよびFORTRANがずっとフリーフォーマットであり、それも50年以上前にニールとバズが月面を歩いた何年も前からそうだったことを考えると、皮肉に思えます。

今日のRPG IVは、私たちのお気に入りのプラットフォームでの開発にとって適切な選択肢であり、きちんと仕事をするのに必要となるものは、ほぼすべて備わっています。ここ数年のRPG IVにおける組み込みSQLに対する機能改善を見てみると、RPGとSQLがあれば、他の何かを使用する必要はないとさえ思えてきます。

RPG IVの機能改善トップ10

フリーフォーマット サポートは、v7r1の時代に完成されましたが、RPG IVの発表以降で最大のRPGの機能改善であることに誰も異論はないでしょう。そのほかにも、ここ何年かの間に導入された、RPG IVのさらなる改善につながる重要な新機能はたくさんあります。それでは、私のお気に入りのRPG IVの新機能トップ10を見て行きましょう。

  1. 最大フィールド長は16メガバイト

    RPG IIおよび初期RPG IIIの最大フィールド長は256バイトでした。今日では、RPG IVの固定および可変長フィールドは、最大16メガバイトにすることができます。初期RPG IVの32kバイトおよびより最近の64kバイトの長さ制限はなくなり、開発者は、どうやってコンパイラーに発覚せずに64kバイト以上使用させようかと考えなくて済みます。

  2. 動的配列

    1990年代に、私はRPG IVの動的配列サポートを「発明」しています。私が発明した手法は、「auto extend」属性を指定してユーザー スペースを使用し、RPG IVポインターを使用してRPG IV配列をユーザー スペースにマッピングするというものでした。この手法は20年間にわたって非常にうまく機能してくれました。今日では、RPG IVには、組み込みの動的配列サポートがあります。コンパイラーの中でどのように処理しているか私にはまったく分かりませんが(おそらくユーザー スペースではないでしょう)、きちんと機能します。通常の配列や私の手法を使用する場合と比べて、構文が多少複雑なだけです。

  3. SQL LOBサポート

    厳密にはRPG IV言語の改善とは言えませんが、LOB(ラージ オブジェクト)SQLデータ タイプのサポートは、今日および将来のWebアプリケーションにとって重要なこととなっています。Webコンテンツの送受信は、もちろんRPG IVフィールドの16MB制限の範囲内に収めることができますが、より大きな値を別にしても、今後どうなるのかは分かりません。IBM提供のWebサービスといったようなものでは、SQL UDF(ユーザー定義関数)およびUDTF(ユーザー定義表関数)は、ラージ オブジェクト(LOB)データを返します。このデータは数ギガバイトになることもあるため、RPG IVのLOBサポートはそれを処理する鍵となります。

    DCL-S(Declare)ステートメントのSQLTYPE(SQL_CLOB)キーワードは、プリコンパイラーにLOBデータを処理するコードを生成させます。開発者は、SQLステートメントのINTO節からその変数へデータを移動させることができます。そのデータを直接IFSへ出力するには、CLOBまたはBLOBが自動的にIFSに保存されるようにするSQLTYPE(SQL_FILE)キーワードを使用すれば簡単に行うことができます。また、GET_CLOB_FROM_FILE()というSQL関数もありますが、これはほぼその名前が示す通りの処理を行います。

    CLOBは、SQL VALUESまたはSQL SETステートメントを使用して、RPG VARCHARまたは固定長フィールドに移動することができます。CLOBが比較的小さいものでない場合は、SQL SUBSTR()関数を使用してCLOBの一部分を抽出するのというが一般的でしょう。実際、45メガバイトのコピーは行いたくないものです。一度に一部分というのが、より現実的なやり方と言えます。IBM iでは、LOBはLOBロケーターにマッピングされがちです。これらは、実質的には、LOBコンテンツが保存されている場所へのハンドルです。したがって、SUBSTRでLOBロケーターが指定されると、システムは、要求されたバイト数を取り出すためにSQLエンジンが使用する数値である4バイトの整数を渡します。これにより、RPGおよびSQLが膨大な量のデータを移動するのを防ぐことができます。

    たとえば、EXEC SQL VALUES SUBSTR( :UPSLABEL, 1, 500) INTO :LAB1;というようなコードを実行した場合、実際にはLOBロケーターが参照されて、UPSLABELコンテンツの500バイトが取り出されます。何キロバイトまたは何メガバイトものデータセット全体を一度に移動するより、はるかに良いやり方です。

    厳密に言えば、これはDB2 for iの機能かもしれませんが、RPG IVに多くの有用な機能をもたらしているのは確かです。

  4. サブプロシージャーの多重定義

    何十年も前から、C++には、関数の多重定義と呼ばれる機能がありました。1つの関数(またはサブプロシージャー)を指定して、デフォルト パラメーターと代替パラメーターの両方の選択できるようにするものです。多重定義では、3つのパラメーターを持つXYZという名前のサブプロシージャーを書いてから、2つ、3つ、4つでも、必要だけのパラメーターを持つ2つ目のXYZを書くこともできます。オプションのパラメーターとは異なり、このサポートでは、重複するサブプロシージャー名を使用して異なるパラメーター タイプを処理することができます。コンパイラーは、サブプロシージャーに渡されたパラメーターの名前およびデータ タイプに応じて、適切なサブプロシージャーのインラインの呼び出しを指定します。

    RPG IVのプロシージャー多重定義のサポートでは、多少異なる構文が使用されます。RPG IVでは重複するプロシージャー名を使用できないため、それぞれのプロシージャー名は一意の名前にする必要があり(慣例的には、プロシージャー名にパラメーター タイプまたはパラメーター名の接尾辞を追加して変化を付ける)、OVERLOADキーワードを使用して様々なプロシージャー名を1つのOverloadedプロシージャーにグループ化する必要があります。戻り値が一貫している場合は、関連のあるプロシージャー名ごとに別々のプロトタイプでOVERLOADキーワードを指定します。

    RPG IVのOverloadedプロシージャーの構文は多少独特ですが、それは「RPGらしさ」でもあり、このサポートは良かったと思います。

  5. ON-EXIT

    ここ数年の機能強化の中であまり目立たないものですが、ON-EXITを挙げておきたいと思います。この奇妙にもシンプルな命令コード(パラメーターを持たず、対となるべき「END-EXIT」もありません)は、メインラインの演算の最下部、またはサブプロシージャーの最後(またはその両方)に置かれます。ON-EXITは、プログラムまたはサブプロシージャーがどこでどのように終了したかにかかわらず、on-exit命令コードに後続するコードを呼び出します。ファイルを開いている場合、ON-EXITの後にCLOSE命令コードを追加することで、確実にファイルが閉じられるようにすることができます。ON-EXITを使用して、割り当てているメモリーを解放または解除することもできます。

    初めてON-EXITを目にしたときは、少しシンプル過ぎて、もっと複雑な実装を見落としているに違いないと思ったほどでした。本当にそれくらいシンプルなのだと分かったときは、うれしい驚きでした。ON-EXITを追加し、ルーチン終了時に必ず行いたい処理をコーディングするだけです。これは、エラー コードや「万が一」の処理である必要はありません。ON-EXITの後で、SQLカーソルを閉じる、印刷またはデータベース ファイルを終了する、あるいはメモリーを解除するなど、通常のタスクを行わせることができます。ON-EXITの追加は、どのように終了したかに関係なく、プログラム終了時に確実にそうしたルーチンが実行されるようになるというだけです。

  6. 長いフィールド名

    25年ほど前にRPG IVが初めて登場したとき、10文字のフィールド名は、RPG IIIの哀れなほど短い6文字の名前と比べて非常に長いと思えたものでした。そして今日では、RPG IVは、あらゆるプログラミングの言語で最も長いフィールド名をサポートしていると言ってよいのではないでしょうか。RPG IVは、最大4096バイトのフィールドおよびその他のID名をサポートしています。フィールド、配列、およびデータ構造の場合は、4Kの一意の名前で、サブプロシージャーの場合は、制限は255ぐらいです。他にも長い名前のサポートをうたっている言語がありますが、ほとんどは、一意であるのは最初の16または32文字までに限られ、残りの部分は無視されます。フィールド名の長さではRPG IVが一番です。

  7. 組み込みのEBCDICからASCIIへの変換機能

    このことはご存じでなかったでしょうか。RPG IVでフィールドを宣言するとき、CCSIDの値とともにCCSIDキーワードを指定することができます。たとえば、

    DCL-S weboutput varchar(200) ccsid(1208);

    この場合、このフィールドにはASCIIデータが格納されるとコンパイラーは考えます。CCSIDキーワードがない場合、コンパイラーは、フィールドには、プログラムがコンパイルされたときに使用されていたジョブのCCSIDのデータが格納されると考えます。

    それでは、標準的な変換を行ってみましょう。標準的な北米EBCDIC(CCSID(37))をUTF-8 ASCII(CCSID(1208))に変換してみます。それを行うためには、次のような「複雑な」命令を実行する必要があります。

    weboutput = 'Hello World';

    実はこれだけです。RPG IVは、自動的にターゲットのフィールドのCCSIDにデータを変換してくれます。WEBOUTPUTフィールドにCCSID UTF-8を指定していたため、EBCDICリテラルは暗黙的なEVAL命令の間に変換されたというわけです。同様に、ASCIIからEBCDICへの変換も、同じくらい簡単に行えます。たとえば、Webから受け取ったデータです。

    ほとんどのアプリケーションが、HTTPPOSTCLOB()のようなWebサービスからの入力値にCCSIDを指定していないと思います。そのため、ASCIIデータが、EBCDICフィールド内にASCIIとして格納されるかもしれません。こうした状況に対して私が思い付いた対応策があります。それにはポインターが必要となるため、ポインター恐怖症の方は、ここを飛ばして先に進んでください。

       Dcl-s webdata char(500) ccsid(1208) based( pEBCDIC );
       Dcl-s rtnBuffer char(500);
    Dcl-s pEBCDIC pointer inz(%ADDR(rtnBuffer);
    Dcl-s clearData char(500);
    
      // call web services and receive ASCII data in the RTNBUFFER field.
       EXEC SQL select responseData into :rtnBuffer
                FROM table(httpPOSTCLOB( blah, blah blah);
    
        clearData = webData;  // Copy the ASCII content to an EBCDIC field.

    このWebサービス ルーチンの呼び出しでは、RTNBUFFERフィールドにデータが返されます。RTNBUFFERのデータはASCIIですが、CCSIDキーワードがないため、コンパイラーはEBCDICであると考えます。RTNBUFFERを別のフィールドへ移動することで、CCSID変換が強制的に行われるわけではありません。ASCIIフィールドへ移動すると、そのASCIIがASCIIへ変換されます。コンパイラーはEBCDICであると考えているため、おかしな結果が生じます。

    適切に変換するためには、iconv()関数を呼び出すこともできますが、RPG IVではそうする必要はなくなっています。私はCCSID(1208)のWEBDATAフィールドをRTNBUFFERフィールドにマッピングしています。お察しの通り、2つのデータ構造のサブフィールドが同じ位置を占めているということです。おそらく一方はPackedで、他方はCharacterです。EBCDICフィールドをASCIIフィールドでオーバーラップしているため、RPGはASCIIフィールドの内容をASCIIと見なします。そのフィールドを、この例のもうひとつのフィールドCLEARDATAへ移動すると、コンパイラーが私に代わって変換を行ってくれるため、私が行う必要があったのは移動(EVAL)だけでした。

  8. IFSファイルのロードおよび保存

    これはSQLとRPG IVで共通の機能強化ですが、ここで取り上げるに値するものだと思います。今日のRPG IVには、/INCLUDEステートメントを除けば、IFSのネイティブ サポートはありません。けれども、SQLTYPE(CLOB_FILE)を変数に追加すれば、SQLを使用してIFSファイルの内容をRPGプログラムに保存および復元することができます。この方法は、open/read/write/close IFS APIを使用するよりはるかに簡単で、ずっと楽しく使えます。

  9. ほぼ完全にフリーなフォーマット

    取り上げやすいと思われるかもしれませんが、RPG IVは何十年もの間、ほぼフリーなフォーマットでした。けれども、HおよびD仕様書がフリー フォーマットのC仕様書の仲間に加わったことで、固定フォーマットの桁位置に頼ることなくプログラムを書き、プログラム記述の出力仕様書を保存することができるようになっています。実際、V7R1の中期以降は、/FREE /END-FREEステートメントを指定する必要はなくなっています。6桁目と7桁目をブランクにして、8~80桁目を「完全にフリーなフォーマット」のステートメントに使用するだけです。他の言語のように1~5桁目または1~7桁目を使用する必要はあるでしょうか。そうする代わりに、1行目の1桁目に*FREEステートメントを挿入すれば、それらの余った7桁をRPGコードに使用できます。

    入力および出力仕様書はまだレガシーとみなされ、フリー フォーマットでサポートされていませんが、いつの日か出力仕様書が、非データベース ファイル出力(たとえば印刷など)で、多少フリーなフォーマットへの変身を遂げたとしても驚くことはないでしょう。

  10. RPG IV拡張機能としての組み込みSQL

    私の場合、SRCTYPE(RPGLE)でRPG IVソース メンバーを作成することはまずありません。ほとんどいつもSQLRPGLEで作成します。たとえ従来型のファイルI/Oを使用しているとしても、SQLRPGLEへの追加機能を挙げるとすれば、この言語のインターフェースの一覧を広げるようなものです。

    • すべて大文字へ変換:
      • EXEC SQL VALUES UPPER( :CSTNAME ) INTO :CSTNAME;
    • 日付を望ましいフォーマットへ変換:
      • EXEC SQL VALUES VARCHAR_FORMAT(:INVDATE, 'DD Mon YYYY') INTO :PRTDATE;
    • 曜日名を取得:
      • EXEC SQL VALUES DAYNAME(CURRENT_DATE) INTO :TODAY;
    • IBM、サードパーティ、および自社製のユーザー定義関数を使用

まだまだリストは続きます . . . .。

結び

これらの機能を見れば、RPG IVが強力な言語になっていることは誰の目にも明らかであり、組み込みSQLが提供する巨大な機能のライブラリーを合わせたら、RPG IVは無敵だという気がします。今日、RPG IV開発者は、数多くの有用な機能を自由に選んで使用できますが、どのような機能が利用可能なのか知るのは難しいこともあります。ここ数年の間にRPG IVに加わった有用な機能に関する、読者の皆さんのRPG IVの知識を少しでもモダナイズできたのだとすれば幸いです。RPG IVの新機能のうち、皆さんのお気に入りはどの機能でしょうか。Twitterは@SQLiQueryおよび@BobCozzi、メールは cozzi@rpgv.comまで、教えていただければと思います。

あわせて読みたい記事

PAGE TOP