永続クラスのその他のオプション
ここでは、永続クラスに使用できる、その他のオプションについて説明します。
シャード・クラスの定義
データ・ストレージの水平方向の拡張にシャーディングを使用している場合、クラス定義で Sharded クラス・キーワードを使用してシャード・クラスを定義できます。シャード・クラスは永続クラスの 1 つで、データはシャード・クラスタのデータ・ノード間で分散されますが、アプリケーションはローカルの場合と同じようにそのデータにアクセスできます。
シャード・クラスを作成するには、以下の例のようにキーワード Sharded = 1 を設定します。
Class MyApp.Person Extends %Persistent [ Sharded = 1 ]
シャード・クラスが完全に実装されるまでは、オブジェクト側からではなく SQL からシャード・テーブルを作成することをお勧めします。
シャードの詳細は、"シャーディングによるデータ量に応じた InterSystems IRIS の水平方向の拡張" を参照してください。
読み取り専用クラスの定義
オブジェクトを開くことはできても、保存または削除できない永続クラスを定義できます。これを行うには、クラスの READONLY パラメータを 1 に設定します。
Parameter READONLY = 1;
これは、既存のストレージ (既存のグローバルや外部データベースなど) にマップされたオブジェクトを持っている場合にのみ便利です。読み取り専用オブジェクトで %Save() メソッドを呼び出す場合、常にエラー・コードが返されます。
列指向ストレージの使用法
列指向ストレージは、リリース 2022.2 の試験的機能として利用できます。つまり、この機能は実稼働環境ではサポートされていません。今後のリリースへアップグレードした場合は、列指向テーブルのデータを再読み込みする必要があります。
InterSystems IRIS でデータを保存する永続クラスを使用する場合、通常はデータが行単位で保存されます。このストレージ・レイアウトは、データの挿入、更新、削除が頻繁に発生するオンライン・トランザクション処理に適しています。一方、オンライン分析処理の場合は、データを列単位で保存する方が適しています。このような処理ではデータベース全体で特定列のデータを集約しますが、リアルタイムの挿入、更新、削除の頻度は高くありません。例えば、行ストレージを使用している場合、指定したプロパティのすべての値の平均値を計算するには、そのデータベースにあるすべての行を読み込む必要があります。列指向ストレージを使用すれば、そのプロパティの値を収めた列のみを読み込むだけで平均値を計算できます。
クラスで列指向のストレージを使用するには、パラメータ STORAGEDEFAULT = "columnar" を設定します。
Class Sample.BankTransaction Extends %Persistent [ DdlAllowed, Final ]
{
Parameter STORAGEDEFAULT = "columnar";
.
.
.
}
列指向を既定のストレージ・レイアウトとして使用するクラスでは、Final クラス・キーワードまたは NoExtent クラス・キーワードを指定する必要があります。また、そのクラスに直下のサブクラスがあれば、それを明示的に Final として定義する必要があります。
トランザクションベースの処理を使用していても、頻繁に分析クエリの実行対象となるプロパティがわずかな場合は、そのプロパティにのみ列指向ストレージを使用できます。
クラスで単一のプロパティに列指向ストレージを使用するには、そのプロパティにパラメータ STORAGEDEFAULT = "columnar" を設定します。
Class Sample.BankTransaction Extends %Persistent [ DdlAllowed ]
{
// Line below is optional
Parameter STORAGEDEFAULT = "row";
Property Amount As %Numeric(SCALE = 2, STORAGEDEFAULT = "columnar");
Index BitmapExtent [ Extent, Type = bitmap ];
.
.
.
}
あるクラスに行ストレージを使用して、頻繁にクエリの対象となるプロパティに列指向インデックスを作成することもできます。
クラスの特定のプロパティに列指向インデックスを作成するには、そのインデックスにキーワード type = columnar を使用します。
Class Sample.BankTransaction Extends %Persistent [ DdlAllowed ]
{
// Line below is optional
Parameter STORAGEDEFAULT = "row";
Property Amount As %Numeric(SCALE = 2);
Index AmountIndex On Amount [ type = columnar ];
.
.
.
}
一般的なインデックスの詳細は、以下の "インデックスの追加" を参照してください。
列指向のストレージの詳細は、"高性能スキーマの定義" の "SQL テーブルのストレージ・レイアウトの選択" を参照してください。
インデックスの追加
インデックスは、永続クラスのインスタンス全体にわたる検索を最適化するメカニズムを提供します。クラスに関連付けられたデータのうち、要求されることが多いデータの一部をソートしてインデックスが定義されます。これらは、パフォーマンス・クリティカルな検索のオーバーヘッドを削減するのに非常に役立ちます。
インデックスは、インデックスが定義されるクラスの全範囲を自動的にカバーします。Person クラスにサブクラス Student がある場合、Person で定義したすべてのインデックスでは、Person オブジェクトと Student オブジェクトの両方が対象になります。Student クラスで定義したインデックスでは、Student オブジェクトのみが対象になります。
インデックスは、そのクラスに属する 1 つまたは複数のプロパティでソートできます。これは、返される結果の順序を制御するのに大変便利です。
またインデックスは、ソートされたプロパティに基づいて、クエリで頻繁に要求される他のデータを格納できます。インデックスの一部として他のデータを含めることによって、インデックスを使用するクエリの性能が大きく向上します。メイン・データ・ストレージにアクセスしなくても、クエリがインデックスを使用して結果セットを生成できます。(以下の Data キーワードを参照してください。)
インデックス定義をクラスに追加すると同時に、そのインデックスにデータを構築するには、ALTER TABLE 文、CREATE INDEX 文、または DROP INDEX 文を使用する以外に方法はありません。クラス定義にインデックスを追加する場合は、インデックスを手動で構築する必要があります。インデックスを手動で構築する方法の詳細は、"インデックスの構築" を参照してください。
インデックスの追加情報は、"インデックスの定義と構築" の、特に "インデックスを付けることができるプロパティ" のセクションを参照してください。"インデックス定義" も参照してください。
外部キーの追加
対応する永続クラスの外部キーを定義して、テーブル間の参照整合性を強制できます。外部キー制約を持つテーブルを変更する際に、外部キー制約が確認されます。外部キーを追加する方法の 1 つは、クラス間にリレーションシップを追加することです。"リレーションシップの定義と使用" を参照してください。クラスには、明示的な外部キーも追加できます。詳細は、"外部キーの使用法" および "外部キー定義" を参照してください。
トリガの追加
InterSystems SQL はトリガの使用をサポートしているので、永続クラスに関係するすべてのトリガは、クラスの SQL プロジェクションの一部として含まれています。
トリガは、特定のイベントが InterSystems SQL で発生するときに実行されるコード・セグメントです。InterSystems IRIS® データ・プラットフォームは INSERT コマンド、UPDATE コマンド、および DELETE コマンドの実行をベースにしたトリガをサポートします。トリガ定義により、指定されたコードは関連するコマンドが実行される直前、または直後に実行されます。各イベントは、実行順序が指定されていれば複数のトリガを持つことができます。
Foreach = row/object を使用してトリガが定義されている場合、トリガはオブジェクト・アクセスの特定の時点でも呼び出されます。"トリガとトランザクション" を参照してください。
トリガは、従来のストレージ・クラス %Storage.SQL によって使用されていた永続メソッドによっても呼び出されます。これは、永続の動作を内部的に実装するために SQL 文を使用しているためです。
ObjectScript からのフィールドの参照
クラス定義内には、SQL で使用される ObjectScript コードを含むことのできる場所が複数あります。例えば、SQL 計算フィールド・コードおよびトリガ・コードは、SQL 内から実行されます。これらの場合、現在のオブジェクトという概念はないので、ドット構文を使用して、特定のインスタンス内のデータにアクセスしたり、データを設定することはできません。代わりに、フィールド構文を使用して現在の行内のフィールドとして同じデータにアクセスできます。
現在の行の特定のフィールドを参照するには、{fieldname} 構文を使用します (fieldname は、フィールド名です)。
例えば、以下のコードは、従業員の給与が、50000 未満かどうかを確認します。
If {Salary} < 50000 {
// actions here...
}
UPDATE トリガ・コードでは、{fieldname} は、更新済みのフィールド値を示します。DELETE トリガ・コードでは、{fieldname} は、ディスク上のフィールドの値を示します。
SQL 計算フィールドの現在のフィールドを参照するには、{*} 構文を使用します。
例えば、以下のコードは、Compensation フィールドのための計算コードに表示され、Salary フィールドおよび Commission フィールドの値に基づいてその値を計算します。
Set {*} = {Salary} + {Commission}
トリガ固有の構文については、"特殊なトリガ構文" を参照してください。
行レベル・セキュリティの追加
一般的なセキュリティに加えて、InterSystems IRIS では 1 行ごとのきめ細かい SQL セキュリティを提供します。これは、行レベル・セキュリティと呼ばれます。行レベル・セキュリティでは、各行に、表示操作を承認されたユーザまたはロールのリストが保持されます。詳細は、"ユーザ" と "ロール" を参照してください。
通常、SQL セキュリティは、テーブルまたはビューに対してユーザまたはロールに SELECT 特権を許可することによって制御します。ロールを使用することにより、セキュリティ・ロールの数がユーザの数を大幅に下回る場合にアクセス制御が単純化します。ほとんどの場合、各ユーザがどの行を選択できるかを制御するのにはビューレベル・セキュリティで十分です。ただし、目的の制御を実現するのに必要なビューの数が非常に多くなる場合は、きめ細かいアクセス制御を行うために別の方法が必要となります。
例えば、病院では各患者が患者固有のデータをオンラインで入手できるようにする場合があります。各患者に個別のビューを作成する方法は、実用的ではありません。代わりに、きめ細かいアクセス制御と InterSystems IRIS のロールベースの認証モデルを組み合わせることにより、行レベル・セキュリティでこのタイプのアプリケーションを効率的に、より安全に作成できます。
以下は、行レベル・セキュリティの使用に関する制約です。
-
行レベル・セキュリティは、永続クラスでのみ利用可能です。
-
行レベル・セキュリティは、InterSystems IRIS サーバ上でインスタンス化されたテーブルでのみ利用可能です。リンク・テーブル (つまり、外部サーバ上でインスタンス化されたテーブル) では利用できません。
-
行レベル・セキュリティは、SQL から行にアクセスする際にのみ適用されます。グローバルに直接アクセスする場合、またはオブジェクト・インタフェースを介してグローバルにアクセスする場合には適用されません。
行レベル・セキュリティの設定
テーブルの行レベル・セキュリティを有効にするには、そのテーブルの投影元のクラスの定義を編集します。
-
クラス定義コードでは、ROWLEVELSECURITY の値を 1 に設定します。以下は、その例です。
ROWLEVELSECURITY = 1;
このパラメータの定義は、行レベル・セキュリティが有効であり、このクラスは生成された %READERLIST プロパティを使用して、行へのアクセスが認証されたユーザおよびロールについての情報を格納することを示しています。
または、パラメータを以下のように定義できます。
ROWLEVELSECURITY = rlsprop;
rlsprop は、同じクラス内のプロパティ名です。この代替手段では、行レベル・セキュリティが有効であり、クラスは指定されたプロパティを使用して、行へのアクセスが認証されたユーザおよびロールに関する情報を格納することを示しています。この場合、次のようにインデックスもクラスに追加します。
Index %RLI On rlsprop;
-
%SecurityPolicy() クラス・メソッドを定義します。このクラス・メソッドは、ビューおよびテーブルの SELECT 特権を前提とし、行の選択を許可されているロールおよびユーザ名を指定します。
%SecurityPolicy() メソッドの構造は以下のとおりです。
ClassMethod %SecurityPolicy() As %String [ SqlProc ] { QUIT "" }
その特性は以下のとおりです。
-
%SecurityPolicy という名前のクラス・メソッドです。
-
文字列 (タイプ %String) を返します。
-
ゼロ個以上の引数を指定できます。このメソッドに引数がある場合は、各引数がクラス内のプロパティ名と一致する必要があり、すべて互いに異なる必要があります。
-
SqlProc キーワードは、メソッドをストアド・プロシージャとして呼び出すことができることを示します。
-
メソッドの QUIT 文は、行を表示できるユーザまたはロールを返します。複数のユーザまたはロールがある場合、QUIT はそれらの名前をコンマ区切りリストにして返す必要があります。例に示すように、Null 文字列を返す場合は、テーブルで SELECT 特権を持つすべてのユーザが行を表示できることを示します。
Important:%All ロールに割り当てられたユーザには、行レベル・セキュリティによって保護されているテーブルの行に対するアクセス権が自動的には付与されません。%All ユーザがこれらの行にアクセスできるようにするには、%SecurityPolicy() メソッドで明示的にそのアクセス権の付与を指定する必要があります。
-
-
クラスおよび依存クラス (存在する場合) をコンパイルします。
既存のデータを含むテーブルへの行レベル・セキュリティの追加
行レベル・セキュリティを既存のデータを含むテーブルに追加するには、まず、"行レベル・セキュリティの設定" で説明する手順を実行します。次に、以下の操作を行います。
インデックスの再構築
ユーザがテーブルのデータにアクセスしている間は、そのテーブルのインデックスを再構築しないでください。これを行うと、クエリ結果が不正確になる場合があります。
テーブルのインデックスを再構築する手順は以下のとおりです。
-
WITH CHECK OPTION 節を持つ定義済みのビューがテーブルにある場合は、DROP VIEW コマンドを使用してこれらのビューを削除します (各行にどのユーザがアクセス権を持つかという設定を更新した後、これらのビューを再作成できます)。
-
管理ポータルのホーム・ページで、[SQL] ページ ([システムエクスプローラ]→[SQL]) に移動します。
-
テーブルを含むネームスペースを選択します。
-
[テーブル] でテーブルの名前を選択します。これにより、テーブルの [カタログの詳細] が表示されます。
-
[アクション] ドロップダウン・リストで、[テーブルのインデックスを再構築] をクリックします。
インデックスの再構築の詳細は、"インデックスの定義と作成" を参照してください。
各行を表示できるユーザおよびロールの更新
これを実行する手順は以下のとおりです。
-
管理ポータルのホーム・ページで、[SQL] ページ ([システムエクスプローラ]→[SQL]) に移動します。
-
テーブルを含むネームスペースを選択します。
-
[クエリ実行] をクリックします。
-
編集可能領域で、テーブルを更新する文を発行します。その形式は以下のようになります。
UPDATE MySchema.MyClass SET rlsprop = MySchema.SecurityPolicy(MySQLColumnName1, ...)
各項目の内容は次のとおりです。
-
MySchema は、そのクラスを含むスキーマ (パッケージ) です。
-
MyClass はそのクラスの名前です。
-
rlsprop は、行を読み取ることができるユーザおよびロールのリストを含むフィールドです。これは、既定では %READERLIST であり、それ以外は ROWLEVELSECURITY パラメータの宣言で指定されるプロパティ名です。
-
SecurityPolicy は、%SecurityPolicy() メソッドの定義で SqlName 値により指定される値です。%SecurityPolicy() メソッドに明示的な SQL 名がなく、そのクラスが MySchema.MyClass である場合、既定の名前は myClass_sys_SecurityPolicy (MySchema.MyClass_sys_SecurityPolicy の完全修飾形式) です。
-
MySQLColumnName1, ... は、%SecurityPolicy() クラス・メソッドで定義される、引数に対応する一連の SQL 列名 (存在する場合) です。
-
-
[実行] をクリックします。
-
必要に応じて、最初に削除したビューを再作成します。
パフォーマンスのヒントおよび情報
%READERLIST プロパティは計算フィールドであり、その値は %SecurityPolicy() メソッドによって決まります。INSERT または UPDATE が実行されるたびに、その行に対して %SecurityPolicy() が呼び出され、%READERLIST の値が自動的に入力されます。
%READERLIST プロパティのコレクション・インデックスが定義され、これにより、クエリ・オプティマイザで使用されて、行レベル・セキュリティが有効になったときのパフォーマンス上の影響を最小化できます。
通常、セキュリティ・ポリシーは複数のコンマ区切りのロールまたはユーザ名を返す可能性があるため、既定では、ROWLEVELSECURITY を 1 に設定すると、コレクション・インデックスは %READERLIST プロパティ (列) に対して定義されます。セキュリティ・ポリシーが複数のユーザまたはロール名を返すことがない場合は、ROWLEVELSECURITY パラメータをオーバーライドし、%RLI インデックスを通常の (非コレクション) ビットマップ・インデックスとして明示的に定義できます。通常は、これによりパフォーマンスが最適化されます。
セキュリティのヒントおよび情報
行レベル・セキュリティを使用する際は、以下のセキュリティ要素に注意してください。
-
行レベル・セキュリティは、テーブルレベル・セキュリティに加えて処理を行います。SELECT、INSERT、UPDATE、または DELETE 文を実行するには、ユーザは該当する行に対してテーブルレベル・アクセスと行レベル・アクセスの両方を許可されている必要があります。
-
ユーザ特権は、実行時 (SQL コマンドの実行を試行する際) に動的に確認されます。
-
更新可能なビューを作成し、WITH CHECK OPTION を指定した場合、そのビューの INSERT 処理は、挿入される行が、ビューで指定された WHERE 句を満たすかどうかを確認します。さらに、行レベルのセキュリティを持つテーブルからビューを作成している場合、ビューで SELECT * FROM のコマンドを発行した際、ビューの WHERE 句または暗黙の行レベルのセキュリティ述語により行を表示できないと、INSERT は失敗します。
-
行へアクセスできる場合は、%READERLIST フィールド (または、行を表示できるユーザおよびロールのリストを保持する任意のフィールド) の値を変更できます。これは、行に対する自分自身のアクセス権を直接的または間接的に削除するアクションを実行できることを示します。
-
行の挿入を試みた際に、その挿入が仮に行レベル・セキュリティが定義されていない状態で UNIQUE 制約に違反するようなものであれば、行レベル・セキュリティが有効なことによりたとえ制約のエラーを引き起こす行が更新トランザクションからは参照できないとしても、行の挿入は制約に違反することになります。