Caché プログラミングの概要
このドキュメントは、Caché に精通していないプログラマや、一部の種類の Caché プログラミングにのみ精通しているプログラマに概要を示すことを目的としています。これはチュートリアルではなく、Caché ツールキットの要素の概要であり、これらの要素が相互にどのように適合するかを示しています。このドキュメントを読むと、使用可能なオプションの概念と、それらのオプションの詳細情報の入手先がわかります。
このドキュメントでは、クライアント側プログラミングではなく、サーバ側プログラミングに焦点を当てています。多様な Caché 言語バインディングにより、Caché サーバは、多数の異なる言語で記述されたクライアントと連動できます。そのようなクライアントを作成または維持する場合、このドキュメントの大部分は参考にならないと思われます。
この章では、Caché サーバ側プログラムで使用できる言語要素の概要と、Caché でそれらの要素がどのように組み合わされているかを示します。以下のトピックについて説明します。
はじめに
Caché は高性能のオブジェクト・データベースであり、いくつかの汎用プログラミング言語が組み込まれています。多重プロセスをサポートし、並列処理の制御が可能です。各プロセスは、データに直接アクセスします。
Caché では、自身の嗜好およびアプリケーションの履歴に合わせて、ルーチン、クラス、またはそれらの組み合わせを記述できます。どのような場合でも、格納されるデータは最終的に、グローバルという構造に含まれます (これらについては、この章内の後述のセクションで説明します)。Caché プログラミングには、以下の機能があります。
-
ルーチンとクラスは、相互に交換して使用でき、複数の言語で記述できます。
-
ルーチンとクラスはお互いに呼び出すことができます。
-
クラスには、オブジェクト指向の機能が用意されています。
-
データベース・ストレージは、すべての Cache プログラミング言語が統合された部分です。
-
クラスは、プログラミングが簡素化されるようにデータを永続化できます。永続クラスを使用する場合、データは、オブジェクト、SQL テーブル、およびグローバルとして同時に使用できます。
-
グローバルには、ルーチンとクラスのどちらからでも直接アクセスできます。つまり、完全に目的どおりにデータを格納およびアクセスできる柔軟性を備えています。
古い Caché アプリケーションは、Caché でクラス定義がサポートされる前に記述されたため、ルーチンのみで構成されています。それとは対照的に、新しいアプリケーションには、ほとんどすべてがクラスで記述されているものもあります。ユーザのニーズに合ったアプローチを選択できます。
ルーチン
Caché でルーチンを作成するときには、ルーチンごとにプログラミング言語を選択できます。選択肢は以下のとおりです。
-
ObjectScript。これは、ISO 11756-1999 標準の M プログラミング言語の上位集合です。M 言語プログラマは、既存の M アプリケーションを、変更を加えることとなく Caché で実行できます。
これは、Caché ルーチンに最もよく使用されている言語です。
-
Caché MVBasic は、MultiValueOpens in a new tab の実装です。これには、さまざまな MultiValue の実装で使用されるコマンド、関数、および演算子が含まれており、複数のエミュレーション・モードがサポートされているため、使い慣れている構文を使用できます。
-
Caché Basic は、Basic の実装です。
これらの言語がどのようなものであるかを見ておくことは有益であるため、ObjectScript で記述されたルーチンの一部を以下に示します。
SET text = ""
FOR i=1:5:$LISTLENGTH(attrs)
{
IF ($ZCONVERT($LIST(attrs, (i + 1)), "U") = "XREFLABEL")
{
SET text = $LIST(attrs, (i + 4))
QUIT
}
}
IF (text = "")
{
QUIT $$$ERROR($$$GeneralError,$$$T("Missing xreflabel value"))
}
以下に示すのは、MVBasic で記述されたルーチンの一部です。
#PRAGMA ROUTINENAME=ADDROUTINENAME
$OPTIONS CACHE
OPEN 'VOC' TO F.VOC ELSE STOP
BAD.LST = ''
* If there is an active select list use it, otherwise get file names from the VOC
IF SYSTEM(11) > 0 THEN
NBR.PROG.FILES = SYSTEM(11)
END ELSE
EXECUTE 'SELECT VOC WITH F6 LIKE B...' CAPTURING RES RETURNING NBR.PROG.FILES
END
IF NBR.PROG.FILES THEN
CRT 'THERE ARE ':NBR.PROG.FILES:' PROGRAM FILES'
EXECUTE 'SAVE-LIST PGM.FILES' PASSLIST
READLIST ALL.PROG.FILES FROM 'PGM.FILES' ELSE STOP
HOW.MANY = DCOUNT(ALL.PROG.FILES,@AM)
FOR I = 1 TO HOW.MANY
THIS.PROG.FILE = ALL.PROG.FILES<I>
CRT
...
以下に示すのは、Caché Basic で記述されたルーチンの一部です。
' display an ordered list of matches
' user can enter full or partial name, full or partial phone, or a valid date
' pick from a list of matches, and EDIT their choice
Option Explicit
dim id, name, phone, intdob, matches
public sub main()
dim done
do
getsubmit(id, done) ' let user submit a string for lookup
if id = 0 then continue do
display(id, "table") ' display the chosen person
edit(id) ' edit the chosen person
loop until done
end sub
private sub getsubmit(ByRef id as %Integer, ByRef done as %Boolean)
' ask user what to search for, and take appropriate action
dim submit
id = 0 : done = False
println : input "Lookup: ", submit : println
if (submit = "") then ' user entered nothing
done = True
exit sub
end if
' figure out what user entered
...
次の章では、ObjectScript の概要を説明します。このドキュメントでは、Caché MVBasic と Caché Basic の詳細には触れません。
クラス
Caché ではクラスもサポートされます。システム・クラスを使用することも、独自のクラスを定義することもできます。
Caché では、プロパティ、メソッド、パラメータ (他のクラス言語では定数と呼ぶ) など、よく知られたクラス要素をクラスに含めることができます。また、トリガ、クエリ、インデックスなど、クラスでは通常定義されない項目を含めることもできます。
クラス定義を以下に示します。
Class Sample.Employee Extends Person
{
/// The employee's job title.
Property Title As %String(MAXLEN = 50);
/// The employee's current salary.
Property Salary As %Integer(MAXVAL = 100000, MINVAL = 0);
/// A character stream containing notes about this employee.
Property Notes As %Stream.GlobalCharacter;
/// A picture of the employee
Property Picture As %Stream.GlobalBinary;
/// This method overrides the method in the Person class.
Method PrintPerson()
{
Write !,"Name: ", ..Name, ?30, "Title: ", ..Title
Quit
}
}
メソッドごとに、そのメソッドの定義で使用するプログラミング言語を指定できます。ただし、簡略化するために、通常は 1 つのクラス内のメソッドすべてで同じ言語を使用します。既定は ObjectScript であり、次の章ではその概要を説明します。他の選択肢としては、Caché MVBasic、Caché Basic、および Transact-SQL があります。(クライアント側プログラミング用には他の選択肢もありますが、このドキュメントでは説明しません。)
クラスはルーチン内から使用できます。例えば、以下はルーチンの一部を示しています。その中で、Sample.Person クラスを参照します。
//get details of requested person; print them
showperson() public {
set rand=$RANDOM(10)+1 ; rand is an integer in the range 1-10
write "Your random number: "_rand
set person=##class(Sample.Person).%OpenId(rand)
write !,"This person's name: "_person.Name
write !,"This person's age: "_person.Age
write !,"This person's home city: "_person.Home.City
}
同様に、メソッドはルーチン内のラベルを呼び出すことができます。以下はその例です。
Method DemoRoutineCall(input) as %String
{
Set value=$$function^myroutine(input)
Quit value
}
“クラス・プログラミングの基本的な考え方” の章では、この種のプログラミングの経験がない読者のために、クラス・プログラミングの簡単な概要を説明します。その後の章では、Caché のクラスと Caché の永続クラス固有の機能について説明します。
グローバルの概要
Caché では、他のプログラミング言語にはない特殊な変数がサポートされています。それがグローバル変数です (通常、単にグローバルと呼ばれます)。Caché では、グローバルという用語は、そのデータが、このデータベースにアクセスするすべてのプロセスで使用可能であることを示します。その使用は他のプログラミング言語とは異なっており、他のプログラミング言語では、グローバルは “そのモジュールのすべてのコードで使用可能であること” を意味します。
グローバルのコンテンツは、Caché データベースに保存されます。次の章ではこれらをより詳しく紹介しますが、ここでは、以下の点のみを理解しておいてください。
-
グローバルは、一連のノード (1 つのノードのみの場合もある) で構成されており、それらは添え字で識別されます。
各ノードは値を 1 つ格納することができます。
-
ObjectScript、Caché Basic、および Caché MVBasic には、グローバルのノードの繰り返し処理を行い、すばやく値にアクセスする関数が含まれています。
-
グローバルは、データベースに自動的に格納されます。グローバル変数のノードに値を割り当てると、そのデータは直ちにデータベースに書き込まれます。
-
グローバルのコンテンツは、ObjectScript のコマンド、または管理ポータルを使用して表示できます。
データへのアクセス方法
Caché では、データベースにグローバル以外は何も含まれていません。このドキュメントで後述するように、コードもグローバルに格納されます。最も低いレベルでは、データへのアクセスはすべて、直接グローバル・アクセスを介して、つまり、グローバルを直接操作するコマンドおよび関数を使用して行われます。
現在実行中の多くのアプリケーションは、Caché にクラスのサポートが組み込まれる前に開発されました。これらのアプリケーションのいくつかは、直接グローバル・アクセスを使用します。他のアプリケーションは、パブリック・ドメインにある FileMan などのカスタム API を使用します。当然ながら、これらの API は内部で直接グローバル・アクセスを使用します。
このドキュメントで後述する永続クラスを使用する場合は、以下の方法のいずれかによって、格納されるデータを作成、変更、および削除できます。
-
%New()、%Save()、%Open()、%Delete() などのメソッドを使用
-
Caché SQL を使用
内部では、システムは常に、直接グローバル・アクセスを使用します。
オブジェクト・クラスが、細かく制御可能なインタフェースを提供することと、Caché の永続クラスが、SQL を使用してクエリ可能なテーブルに投影されることから、クラス・インタフェースを既存のインタフェースに追加する方が望ましい場合がよくあります。グローバルの構造を理解している場合は、この操作を行ってください。
グローバルを使用することの意味
グローバルは、高度に最適化された構造に物理的に格納され、この構造を管理するコードは、Caché が稼働するあらゆるプラットフォームに対して別々に最適化されています。この最適化により、グローバルの処理では高いスループット (単位時間あたりの処理数)、高水準の並行処理 (同時アクセスの総数)、そしてキャッシュ・メモリの有効利用が可能となり、性能に対するメンテナンス (頻繁に行われてきた再構築、再度のインデックス付けや圧縮作業など) も一切必要ありません。グローバルの格納に使用する物理構造は完全にカプセル化されるため、一般的に、物理データ構造を検討する必要はありません。
グローバル・ストレージはスパースです。つまり、データベースに格納されるのは、データ値を持つノードのみです。したがって、多くの場合、Caché に必要な容量は、リレーショナル・データベースに必要な容量の半分未満になります。
グローバルのその他の利点には、以下があります。
-
グローバルの階層構造では通常、リレーショナル・テーブルの場合よりも、現実のデータにより近い形でデータがモデル化されます。
-
スパースという特性は、オーバーヘッドもなく、既存のデータベースを再構築することもなく、新しいフィールドを追加できることを意味します。
-
一度の I/O 処理でより多くのデータの読み書きが可能となり、データがより効率よくキャッシュされます。
Caché SQL
前述したように、Caché は、Caché SQL と呼ばれる SQL の実装を提供します。
Caché SQL は、ルーチン内およびメソッド内で使用できます。これらのコンテキストで SQL を使用するには、以下のツールのいずれか、または両方を使用します。
-
ダイナミック SQL (%SQL.StatementOpens in a new tab クラスおよび %SQL.StatementResultOpens in a new tab クラス)。以下に例を示します。
SET myquery = "SELECT TOP 5 Name,Home_City FROM Sample.Person ORDER BY Age" SET tStatement = ##class(%SQL.Statement).%New() SET tStatus = tStatement.%Prepare(myquery) SET rset = tStatement.%Execute() DO rset.%Display() WRITE !,"End of data"
ダイナミック SQL はどのようなコンテキストでも使用できます。
-
埋め込み SQL。以下に例を示します。
&sql(SELECT COUNT(*) INTO :myvar FROM Sample.Person) Write myvar
最初の行は埋め込み SQL であり、Caché SQL クエリを実行し、myvar と呼ばれるホスト変数に値を書き込みます。
次の行は通常の ObjectScript であり、変数 myvar の値を書き込むのみです。
埋め込み SQL は、ObjectScript で作成されたメソッドおよび ObjectScript ルーチンで使用できます。
マクロ
ObjectScript では、置換を定義するマクロもサポートします。その定義は、値、あるコード行の全体、(##Continue 指示文を含む) 複数の行のいずれかです。マクロは、整合性を確保するために使用します。以下はその例です。
#define StringMacro "Hello, World!"
write $$$StringMacro
マクロで何を行えるのかを示すために、内部で使用されるマクロの定義の例を以下に示します。
#define output1(%str,%lf,%indent) do output^%fm2class(%str,%lf,%indent,$$$display)
このマクロは、多くのマクロと同様に、引数を受け入れます。また、別のマクロを参照します。
マクロは、ルーチン内およびクラス内で使用できます。システム・クラスのいくつかでは、マクロが広範に使用されています。
インクルード・ファイル
ルーチンの中でマクロを定義し、それを同じルーチンの中で後で使用できます。より一般的には、それらは中央の場所で定義します。このためには、インクルード・ファイルを作成して使用します。インクルード・ファイルは、マクロを定義し、他のインクルード・ファイルをインクルードできます。
以下に示すのは、システム・インクルード・ファイルの一部です。
/// Create a success %Status code
#define OK 1
/// Return true if the %Status code is success, and false otherwise
/// %sc - %Status code
#define ISOK(%sc) (+%sc)
/// Return true if the %Status code if an error, and false otherwise
/// %sc - %Status code
#define ISERR(%sc) ('%sc)
以下に、別のインクルード・ファイル全体を示します。
#include %occCacheDirect
#include %occExtent
#include %occTransaction
#include %occInclude
#include %msql
#include %cspInclude
ここでは以下の操作を実行できます。
-
ルーチンの最初でインクルード・ファイルをインクルードします。そのルーチンは、インクルード・ファイル内で定義されているマクロを参照できます。
-
クラスの最初でインクルード・ファイルをインクルードします。そのクラスのメソッドは、そのマクロを参照できます。
-
メソッドの最初でインクルード・ファイルをインクルードします。そのメソッドは、そのマクロを参照できます。
インクルード・ファイルをクラス定義に含めるために使用する構文は、少し異なります。このドキュメントで後述する、“クラス定義におけるマクロとインクルード・ファイル” を参照してください。
これらのコード要素がどのように連携しているか
この章で紹介するコード要素が Caché でどのように使用されるのかを理解しておくと役立ちます。
ObjectScript、Caché SQL、Caché MVBasic、マクロ、クラス定義、ルーチンなどを組み合わせて使用できるのは、記述したコードを Caché が直接使用しているのではないからです。代わりにコードをコンパイルすると、システムによって、使用するコードが生成されます。それが OBJ コードであり、Caché 仮想マシンによって使用されます。
複数のステップがあります。上記の図は、それらの全般的な考え方を示しています。ステップを詳細に理解しておく必要はありませんが、以下の点を覚えておくことをお勧めします。
-
CSP エンジンは、CSP ファイルをクラス定義に変換します。
-
クラス・コンパイラはクラス定義を使用して、INT コード、Caché MVBasic コード、および Caché Basic コードを生成します。生成するコードはクラスのメソッドを定義するために使用する言語によって異なります。
場合によっては、コンパイラは最初にクラスを、追加のクラスを生成および保存するための基盤として使用します。これらのクラスは、スタジオで調べることができますが、変更はしないでください。これは、例えば、Web サービスおよび Web クライアントを定義するクラスをコンパイルしたときに行われます。
クラス・コンパイラは、各クラスのクラス記述子も生成します。これは、システム・コードが実行時に使用します。
-
プリプロセッサ (マクロ・プリプロセッサ または MPP と呼ばれることもあります) は、(図には示されていない) インクルード・ファイルを使用し、マクロを置換します。また、ObjectScript ルーチン内および MVBasic ルーチン内の埋め込み SQL も処理します。
これらの変更は、一時的な作業領域で行われ、コードが変更されることはありません。
-
追加のコンパイラが、ObjectScript ルーチン用の INT コード、および MVBasic ルーチン用の MVI コードを作成します。この層は、中間コードともいいます。この層では、データへのすべてのアクセスは、直接グローバル・アクセスを介して行われます。
INT コードと MVI コードはどちらもコンパクトですが人間が読める形式です。このドキュメントの後のセクションでは、診断のために役立つこのコードを見つける方法を示します。
Caché Basic には中間コードがないことに注意してください。
-
INT コード、MVI コード、および Caché Basic は、OBJ コードの生成に使用されます。
このコードは、Caché 仮想マシンによって使用されます。コードのコンパイルが完了したら、ルーチン、INT コード、および MVI コードはコードの実行に不要となります。
-
クラスをコンパイルしたら、それらを配置モードにすることができます。Caché には、内部クラスを削除し、指定されたクラスの中間コードを削除するユーティリティがあり、このユーティリティはアプリケーションを配置するときに使用できます。
Caché システム・クラスを調べると、クラスによっては配置モードになっているため、見えないことがあります。
ルーチンとクラス定義はすべて、生成済みコードとして同じ Caché データベースに格納されます。これにより、コードの管理が容易になります。Caché のスタジオには一連の堅牢なソース・コントロール・フックが用意されており、インターシステムズの開発者はそれらを長年にわたって使用してきました。これらのフックはユーザも使用できます。概要については、このドキュメントで後述する “開発ツール” を参照してください。