2013.3.21
カーステン・フレンズバーグ

ファイル入出力へのネイティブ・アクセス

RPGネイティブのレコード・レベル・アクセスでデータ構造を使用する

今回はRPGネイティブのレコード・レベル・アクセスでデータ構造を使用してファイルの入出力を行う方法について説明します。

レコード・レベル・アクセスとデータ構造

RPG言語に元来備わっている特徴の一つがネイティブあるいはレコード・レベルのファイル・アクセス機能です。ほとんどの方がこのタイプのデータ・アクセスをもう長年に渡ってお使いのことと思います。しかしご想像の通り、RPGを使っている人の多くは純粋なSQLではなくレコード・レベル・アクセスを継続して使用することに強く反対しています。本稿ではその議論自体には触れません。私自身は「仕事に適したツールを使う」というタイプの開発者なので、状況に応じてネイティブのデータ検索手法も組み込みSQLによるデータ検索手法も両方使用します。

この両者の違いはさておき、レコード・レベル・アクセスを使用するとき、データベースからデータを受け取ったり戻したりするのにデータ構造を使用していますか?もしデータ構造を使用していないのであれば、データ構造の使用を是非検討してみてください。

ファイル操作のためにデータ構造を使用すると良いのはパフォーマンスであるという点を誰もがお気に入りのようです。入出力が集中するようなバッチ・アプリケーションにとって、データ構造を使用することでパフォーマンスが大幅に向上することがあります。その理由は、それぞれの状況において何が起こるのかを理解してしまえばきわめて簡単です。従来から使用されているレコード・レベル・アクセスを使用すると、RPGコンパイラはF仕様に指定されている各ファイル中に含まれているフィールドに基づいて大域変数を定義します。レコードが読み出されたり書き込まれたりするとき、RPGランタイムは各フィールドをそれぞれ個別にデータベース用のメモリ・バッファに読み込んだり書き出したりします。フィールドを多数持ったファイルではこの処理は比較的高価なものになります。

ファイル操作にデータ構造を使用するとバッファの中身をすべてデータ構造に移すか、またはその逆にデータ構造の中身をすべてバッファに移します。これは各フィールドに対して個々にEVAL操作をするのか、レコード全体に対して1回のEVAL操作をするのかの違いと考えてもよいでしょう。100カラム以上を持つファイルがあって、100万かそこらのレコードを読み込み、差異があれば更新するという処理をするとなるとこれは大きな差となります。

個々のフィールドごとでなくレコード全体を移す方法は他にも利点があります。不正なデータが入っているレコードを読み込もうとすると、あの嫌な10進数データエラーに出くわします。この時、このエラーは各フィールドのデータを移すRPGランタイムの処理中で発生するのです。このエラーにプログラムで対処するのにはイライラさせられます。データ構造を使用すると個々のフィールドに「触らない」で済みますので次の2つの利点があります。まず、プログラムが不正なデータを含むフィールドを使わずに済めばエラーは発生しませんので、アプリケーションがよりエラーのないものになります。次に、プログラム中で数値フィールドが初めて使用された時にデータに10進数エラーが発生しないかどうかを監視できるので、エラーが発生してもプログラムを行儀よく終了させることができます。私は通常、エラーのあるデータを持つキーの値をログしてそのデータを素早く修正できるようにしています。以前は、数100万レコードあるテーブルの中から10進数フィールドが空白となっている1つのレコードを探すのに何時間も無駄な時間を使っていました。

データ構造を使用すると、よくあるもう一つの落とし穴である名前の衝突を避けることができます。名前の衝突は、同じ名前のフィールドが複数のファイルで使用されているときに発生します。各ファイル中で同じタイプと同じ長さのフィールドを定義すると、プログラムは正常にコンパイルされます。この場合、一方のデータを操作しているときにもう一方のデータをうっかり踏みつけてしまわないように注意しなければなりません。フィールドを別々に定義した場合は、プログラムのコンパイルに失敗します。この名前の衝突を避けるために、私は通常F仕様でPREFIXキーワードを使用します。

名前の衝突は必ずしもいつも悪いというわけではありません。たとえば、プリント・ファイル中のフィールドにデータ・ファイル中のフィールドと同じ名前を付けると、レポートの作成のコードをかなり節約できます。各ファイル用の入出力操作の条件を満たした異なるデータ構造を使用すると両方にとって良いことになります。条件を満たしたデータ構造中のサブフィールドに対する参照はすべてデータ構造名を含んでいるので、個々のファイルに対して別個のデータ構造を作成してデータを別々にしておくことができます。ただし、似たような名前のフィールドのデータを一つのファイルから別のファイルに再配置する必要が生じたときは、EVAL-CORR操作を使用して1行のコードですべてを移動することができます。

IBM i 6.1と7.1では、ファイル操作用のデータ構造を使用しなければならない新しいRPGの機能がいくつかあります。何年にも渡ってRPGのプログラマは、RPGの「サイクル」に依存してたくさんの処理をまるで魔法を使うように行ってきました。実際のプログラムではもうI仕様をコーディングすることはほとんどありませんが、コンパイル・リストを見るとRPGコンパイラが自動的にそうしたコードを挿入していることにお気づきかと思います。その理由はRPGサイクルがその機能の多くをI仕様に依存しているからです。しかしこの利便性には代償が伴います。この時代遅れの仕様に関する要件のせいで、RPGの一部の機能に制限が加わるのです。RPGのすばらしい新機能のいくつかを使用するには、I仕様を使わずにデータ構造を使用しなければなりません。

IBM i 6.1では二つの新しいファイル入出力機能がデータ構造を必要とします。一つ目は、そしてこれが一番大きいのですが、サブプロシージャ中のファイル定義のサポートです。サブプロシージャ内でF仕様をサポートすることで、プロシージャはファイルに対して独自の接続を持つことができます。I仕様もRPGサイクルもサブプロシージャ内には存在しないため、入出力処理に使える唯一のメカニズムはデータ構造ということになります。二つ目の新しい機能は条件を満たしたレコード・フォーマット名を使用できる機能です。この機能のおかげで、同じレコード・フォーマット名に複数のファイル・オブジェクトがあるときにRENAMEキーワードを使用する必要がなくなります。この機能は、レコード・フォーマットを参照するときは必ずファイル名が含まれるという条件で動作します。I仕様は条件を満たすレコード・フォーマット名をサポートしていませんので、レコード・フォーマット名は生成されませんし、RPGサイクルは入出力の処理ができません。

IBM i 7.1では私が気に入っている新しいデータ構造の機能が追加されました。DB2のファイルが長い名前を持ったフィールドをサポートする機能を備えてからかなり経ちます。RPGサイクルは固定フォーマットであるI仕様に依存しているため、10文字を超える名前をサポートできません。これは単にI仕様自体でフィールド名に使用できるカラム数の制限によるものです。しかしIBM i 7.1ではファイルのF仕様または外部で定義したデータ構造定義にALIASキーワードを追加して、長いカラム名がもしあればそれを指すことができます。これを可能にするには、I仕様は生成されないので、入出力操作にはデータ構造を使用しなければなりません。

RPGコンパイラ・チームはRPGプログラマがこうした機能強化に対して準備ができるようにかなり長い時間をかけてきました。実際こうした改良の基礎は、10年前のV5R2にRPGが初めて外部定義のレコード・フォーマットを使用した入出力用のデータ構造を使用できるようにしたときに作られました。こうした新機能を構築するためのその他の特筆すべき追加機能には、EXFMT操作やサブファイル操作を使用したデータ構造の仕様があります。IBM i 6.1における追加の機能強化でさえもサブファイルに対する入出力のための同じデータ構造の使用を可能にしました。

図1: ファイル操作で使用するデータ構造

ファイル入出力用のデータ構造の使用

さてファイル操作になぜデータ構造を取り入れたほうが良いのかについてその理由をいくつか説明しましたので、実際の使用方法についてもう少し詳しくお話しましょう。テクニック自体は特に難しいものではありません。ファイル操作にデータ構造を使用したときのコードがどのようになるかを図1に示しました。さまざまな操作がデータ構造の定義を必要とするので、私がLIKE RECを使用して3つのデータ構造*INPUT、*OUTPUT、*KEYを定義しているのにお気づきでしょう。入力操作(READ、READE、READP、READPE、CHAINおよびREADC)では*INPUTフォーマットを使用してデータ構造を定義する必要があります。出力用にWRITEオプコードを使用する場合は、*OUTPUTフォーマットでデータ構造を定義しなければなりません。UPDATE操作の場合はどちらのフォーマットでもかまいませんが、私はいつも*OUTPUTフォーマットを使用して一貫性を保っています。

さてここで皆さんは「これらの操作をそれぞれ異なる方法で定義しなければならないのはなぜか」とお思いになっているのではないでしょうか。RPGプログラムが使用するほとんどのファイルでは、すべて入力と出力の両方用に定義されたフィールドがありますが、どちらか片方用のフィールドを定義することが可能です。こうした可能性があるので、データ構造に対して正しいフォーマットを使用することでデータが適切なバッファの外観と一致することが保証されます。

三つ目のデータ構造である*KEYは特殊なタイプのデータ構造で、データ構造中のキー・フィールドだけを持っています。このデータ構造はKLISTをフリー・フォーマットで置き換えたものです。私がRPGで初めてフリー・フォーマットを使い始めた時、このタイプのデータ構造が存在していることを知らなかったので、固定フォーマットに切り替えてキー・リストを定義しなければいけないことが大嫌いでした。キー・データ構造には実際にはとてもすばらしい機能がありますが、これについては後で説明します。

図1で、フリー・フォーマット中の最初のコード行は単純なREAD操作です。ファイル名の後にデータ構造名を追加しておいた点に注意してください。こうするとMyFile用の入力バッファをデータ構造MyFile_Inに移動するようにRPGに伝えることになります。これを固定フォーマットで行うには、データ構造名をResult Fieldの位置に置かなければなりません。ファイル操作用にデータ構造を使用する際に行わなければいけないのはこれだけです(他のファイル・オプコードについてもこれは同じです)。

その他の二つの操作では、%KDS組み込み関数を使っている点に注意してください。この関数はデータ構造をキー・リストとして使用するようにRPGに指示します。オプションの二番目のパラメータはデータ構造からいくつのキー・フィールドを用いるかをコンパイラに対して指定します。これにより、いろいろな状況で使用しなければならないキー・フィールドがいくつあるかにかかわらず、ファイルに対する一つのキー・データ構造を使用することができます(この例については後述します)。

キー・データ構造と%KDSのもう一つのすばらしい特徴は、キー・データ構造とファイル中のキー・フィールドとの間で汎用データ・タイプだけが一致しなければならないということです。フィールドの長さやフォーマットが一致しない場合はRPGが変換してくれます。文字データについてはRPGが切り捨てるかフィールドを空白で埋めるかして一致するようにしてくれます。数値データの場合、RPGは一致するように長さと精度を調整して、必要に応じて数値タイプ間で自動的に変換してくれますので、キー・フィールドがパック10進数なのかゾーン10進数なのかを心配する必要はもうありません。また、LIKERECやEXTNAMEを使用してデータ構造を作成する必要もないし、同じファイルに対して作成する必要さえないという点にも注意してください。キーとして使用する独自のデータ構造を定義するか、またはキー・フィールドを持った他のファイルのデータ構造を用いることができます。

図2: ファイル操作用のデータ構造のモックアップ

図2に示した少し大げさに簡素化したモックアップでは、プログラム中でファイル操作用のデータ構造がどのようになっているかが示されています(本シリーズの次回の記事ではもっと複雑な例を説明します)。この例は図1と同じMyFileファイルとそのデータ構造を使用しています。ご覧になっておわかりの通り、CHAINオプコードを使用してMyFile_Inデータ構造中にレコードを入れます。レコードを見つけた場合は、まずそのデータをMyFile_Inデータ構造からMyFile_Outデータ構造にコピーしなければなりません。入力操作と出力操作はほとんどの場合で同じデータ構造を使用することはできないので、データ構造が等価であると設定することによって結果的に入力バッファを出力バッファに移すことになる、ということを忘れないでください。次に、プログラムはMyFileのデータに対して操作を行います。

プログラムの実行が完了すると、MyFileが新しいデータで更新されます。MyFile_InとMyFile_Outに格納されているデータそれぞれの実行前のイメージと実行後のイメージがありますので、レコードを更新する前にデータに変更があったか否かを簡単に調べることができます。これは是非調べてください。というのも、不必要なレコードの更新をせずに済むので、CPUサイクルとジャーナルのエントリを節約できるからです。

次回までに

本稿では、データ構造を一般的に定義し使用するところからファイルの入出力操作に使用するところまでを説明してきました。次回は、データ構造をデータ格納と移送のための主要な手段として使用することで、本当の意味でのILEアプリケーションを使用して構築する方法に関してお話ししてきたことすべてをまとめる予定です。それまで、コーディングをお楽しみください。

ページトップ

ボタン