TimesTenトランザクション・ログAPI(XLA)はC言語関数のセットで、この関数によって、次の処理を実行するアプリケーションを実装できます。
ローカル・データベースの指定した表への変更に関してTimesTenを監視するアプリケーション。
これらの変更の通知をリアルタイムで受信するアプリケーション。
XLAの主な目的は、トリガーにかわる高パフォーマンスで非同期の機能です。
注意: 『Oracle TimesTen In-Memory Database開発者および管理者ガイド』で説明するTimesTenレプリケーション・ソリューションではニーズが満たされない場合、XLA関数を使用してカスタム・データ・レプリケーション・ソリューションを構築できます。 |
この章の内容は次のとおりです。
各XLA関数の詳細は、第9章「XLAリファレンス」を参照してください。
この項の内容は次のとおりです。
ここで説明するXLA関数については、第9章「XLAリファレンス」を参照してください。
TimesTen XLAによって更新レコードがトランザクション・ログ・バッファまたはトランザクション・ログ・ファイルから直接取得されるため、それらの更新レコードは、必要とされるかぎり使用可能です。また、ロギング・モデルによって、複数のリーダーでトランザクション・ログの更新を同時に読み取ることができます。
ttXlaPersistOpen
XLA関数は、でデータベースへの接続をオープンします。
TimesTenは、初期作成時に、アプリケーションがリンクされているTimesTenリリースと同じバージョンに、トランザクション・ログ・ハンドルを構成します。また、ttXlaGetVersion
およびttXlaSetVersion
XLA関数を使用して、以前のXLAバージョンと相互運用することもできます。
アプリケーションによってデータベースが変更されると、TimesTenは、データの変更およびトランザクション・コミットなどの他のイベントを示すトランザクション・ログ・レコードを生成します。
新しいトランザクション・ログ・レコードは、生成されると常にログ・バッファの最後に書き込まれます。
トランザクション・ログ・レコードは、メモリ内のログ・バッファからディスク上のトランザクション・ログ・ファイルへ定期的に一括でフラッシュされます。XLAが初期化されている場合、XLAアプリケーションでは、トランザクション・ログのどの部分がディスクまたはメモリーに存在するかについて考慮する必要はありません。したがって、この章で使用するトランザクション・ログという用語は、トランザクション更新レコードの仮想ソースを指し、これらのレコードが物理的にメモリーまたはディスクに保存されているかどうかは関係ありません。
アプリケーションで、XLAを使用し、データベースへの変更に関してトランザクション・ログを監視できます。XLAは、トランザクション・ログ全体を読み取り、ログ・レコードをフィルタ処理し、目的の表および列への変更が含まれているトランザクション・レコードのリストをXLAアプリケーションに配信します。
XLAは、レコードをディスクリート・トランザクションにソートします。複数のアプリケーションが同時にデータベースを更新している場合、異なるアプリケーションのトランザクション・ログ・レコードがトランザクション・ログに交互配置されます。
XLAは、特定のトランザクションに関連するすべてのトランザクション・ログ・レコードを透過的に抽出し、それらを連続するリストにしてアプリケーションに配信します。
コミット済のトランザクションのレコードのみが返されます。コミット済のレコードは、最後のコミット・レコードがトランザクション・ログに書き込まれる順序で返されます。コミットされていないデータベースへの変更に関連するレコードは、XLAによってフィルタ処理されます。
変更が行われ、その後にロールバックされた場合、XLAは強制終了されたトランザクションのレコードをアプリケーションに配信しません。
これらのXLAの基本概念のほぼすべてを次の例5-1に示し、次にそれぞれの内容を箇条書きで示します。
図5-1に示すトランザクション・ログを例として考えてみます。
例5-1 トランザクション・ログ・レコードの読取り
この例では、トランザクション・ログに次のレコードが含まれています。
CT1
- アプリケーションC
は、表W
の行1を値7.7で更新。BT1
- アプリケーションB
は、表X
の行3を値2で更新。CT2
- アプリケーションC
は、表W
の行9を値5.6で更新。BT2
- アプリケーションB
は、表Y
の行2を値XYZで更新。AT1
- アプリケーションA
は、表Z
の行1を値3で更新。AT2
- アプリケーションA
は、表Z
の行3を値4で更新。BT3
- アプリケーションB
がトランザクションをコミット。AT3
- アプリケーションA
がトランザクションをロールバック。CT3
- アプリケーションC
がトランザクションをコミット。表W
、Y
およびZ
への変更を検出するように設定されているXLAアプリケーションでは次のようになります。
BT2
およびBT3
- 表Y
の行2を値XYZで更新し、コミット。CT1
- 表W
の行1を値7.7で更新。CT2
およびCT3
- 表W
の行9を値5.6で更新し、コミット。この例では、次のことが示されています。
アプリケーションB
およびC
のすべてのトランザクション・レコードが同時に書き込まれます。
トランザクション・ログでは、アプリケーションC
のレコードがアプリケーションB
のレコードより前に書き込まれますが、アプリケーションB
のコミット(BT3
)はアプリケーションC
のコミット(CT3
)より前に書き込まれます。その結果、アプリケーションB
のレコードはアプリケーションC
のレコードより先にXLAアプリケーションに返されます。
XLAは表X
への変更を検出するように設定されていないため、アプリケーションB
による表X
への更新(BT1
)は示されません。
アプリケーションA
による表Z
への更新(AT1
およびAT2
)が示されないのは、コミットされずにロールバックされたためです(AT3
)。
XLAを使用すると、表およびマテリアライズド・ビューの両方への変更を追跡できます。マテリアライズド・ビューによって、複数のディテール表内の選択した行および列への変更を追跡できる単一のソースが提供されます。マテリアライズド・ビューがない場合は、XLAアプリケーションで、すべてのディテール表の更新レコード(アプリケーションにとって重要ではない行および列への更新が反映されているレコードを含む)に対して監視およびフィルタ処理を行う必要があります。
一般に、表またはマテリアライズド・ビューへの変更を追跡するために使用するXLAメカニズムの間に処理上の違いはありません。ただし、非同期のマテリアライズド・ビューの場合、非同期のマテリアライズド・ビューのXLA通知の順序は、関連付けられているディテール表での順序または同期のマテリアライズド・ビューでの順序と同じとはかぎらないことに注意してください。たとえば、ディテール表に2つの挿入が行われる場合、非同期のマテリアライズド・ビューでは逆の順序で挿入が行われることがあります。さらに、マテリアライズド・ビューのディテール表への更新は、削除とその後に続く挿入としてXLAによって扱われることがあります。また、複数の挿入や複数の削除などの複数の操作を組み合せて1つの操作にすることもできます。正確な順序に依存するアプリケーションでは、非同期のマテリアライズド・ビューを使用しないでください。
マテリアライズド・ビューの詳細は、次を参照してください。
『Oracle TimesTen In-Memory Database SQLリファレンス・ガイド』のCREATE MATERIALIZED VIEWに関する項
『Oracle TimesTen In-Memory Databaseオペレーション・ガイド』のマテリアライズド・ビューの理解に関する項
各XLAリーダーは、ブックマークを使用して更新ログ・ストリーム内での位置を維持します。各ブックマークは、ログ・レコード識別子を使用してトランザクション・ログ内の更新レコードを追跡する2つのポインタで構成されています。
初期読取りログ・レコード識別子は、最後に確認されたトランザクション・ログ・レコードを指します。初期読取りログ・レコード識別子はデータベースに保存されるため、データベースの接続、停止および障害が発生しても永続的に維持されます。
現行読取りログ・レコード識別子は、トランザクション・ログから現在読み取られているレコードを指します。
この項の以降の内容は次のとおりです。
「XLAの初期化およびXLAハンドルの取得」で説明するとおり、ttXlaPersistOpen
関数をコールしてXLAハンドルを初期化する場合、tag
パラメータを使用して新しいブックマークまたはシステムに既存のブックマークのいずれかを指定し、options
パラメータを使用してそのブックマークが新しいレプリケートされていないブックマーク、新しいレプリケートされたブックマーク、または既存(再利用)のブックマークのいずれであるかを指定します。この時点で、ブックマークに関連付けられている初期読取りログ・レコード識別子は、データベースから読み取られ、XLAハンドル(ttXlaHandle_h
)にキャッシュされます。この識別子は、トランザクション・ログ内のリーダーの開始位置を示します。
詳細は、『Oracle TimesTen In-Memory Databaseリファレンス』のttLogHoldsに関する説明を参照してください。TimesTen組込みプロシージャは、トランザクション・ログの保留に関する情報を返します。
アプリケーションが最初にXLAを初期化し、XLAハンドルを取得した時点では、現行読取りログ・レコード識別子と初期読取りログ・レコード識別子は、この後の図5-2に示すように、両方ともデータベースに最後に書き込まれたレコードを指しています。
ttXlaNextUpdateまたは
ttXlaNextUpdateWait関数を使用すると、一連のコミット済トランザクションのレコードが、コミットされた順序でトランザクション・ログから返されます(「トランザクション・ログからの更新レコードの取得」
を参照)。図5-3
に示すように、ttXlaNextUpdateをコールするたびに、ブックマークの現行読取りログ・レコード識別子は、最後に読み取られたレコードに再設定されます。現行読取りログ・レコード識別子によって、次にttXlaNextUpdate
をコールする場合の開始位置がマークされます。
ttXlaGetLSN
およびttXlaSetLSN
関数を使用すると、レコードを再度読み取ることができます(「ブックマークの位置の変更」を参照)。ただし、図5-4に示すように、
ttXlaAcknowledge関数をコールすると、ブックマークの初期読取りログ・レコード識別子が現行読取りログ・レコード識別子に永続的に再設定されます。ttXlaAcknowledge
関数をコールして初期読取りログ・レコード識別子を再設定すると、以前に読み取られたすべてのトランザクション・レコードにパージのフラグが設定されます。初期読取りログ・レコード識別子を再設定すると、ttXlaSetLSN
関数を使用して元に戻し、以前に読み取ったトランザクションを再度読み取ることはできません。
注意: ttXlaAcknowledge をコールすると、確認する関連更新レコードがない場合でもブックマークが再設定されます。これは、トランザクション・ログ領域の管理に役立ちますが、処理コストとのバランスを保つ必要があります。XLAでは、トランザクション・ログが一度に1ファイルずつパージされる点に注意してください。処理の詳細は、「ttXlaAcknowledge」を参照してください。 |
データベースで作成されるブックマークの数は64に制限されています。各ブックマークに一度に関連付けることができるアクティブな接続は1つのみです。ただし、ブックマークは、存続期間中に多数の接続と関連付けることができます。アプリケーションによって、接続をオープンし、新しいブックマークを作成し、そのブックマークを接続と関連付け、ブックマークを使用してレコードを読み取り、接続をデータベースから切断し、データベースに再接続して、新しい接続を作成し、この新しい接続を既存のブックマークと関連付け、以前の接続が停止されたトランザクション・ログ・レコードから読取りを続行することができます。
アクティブ・スタンバイ・ペアのレプリケーション・スキームを使用する場合は、ttXlaPersistOpenコールの
optionsの設定に従ってレプリケートされたブックマーク
を使用できます。レプリケートされたブックマークの場合、ブックマークに対する処理は必要に応じてスタンバイ・データベースにレプリケートされます。これにより、フェイルオーバーが発生した場合、より効率的にブックマークの位置をリカバリできます。読取りは、新しいアクティブ・ストアへのスイッチオーバー前に読取りが中断された位置に近いXLAレコードのストリームから再開されます。レプリケートされたブックマークがない場合、古いアクティブ・ストアに返されている多数の重複レコードを読み取る必要があります。
レプリケートされたブックマークを使用する際には、次の順に手順を実行する必要があります。
アクティブ・スタンバイ・ペアのレプリケーション・スキームを作成します。(これは、create active standby pair
操作、またはクラスタウェア管理環境のttCWAdmin -create
コマンドによって行われます。)
ブックマークを作成します。
ブックマークをサブスクライブします。
アクティブ・スタンバイ・ペアを起動します(このとき、スタンバイへの複製が発生し、レプリケーションが始まります)。(これはttRepAdmin -duplicate
コマンド、またはクラスタウェア管理環境のttCWAdmin -start
コマンドによって行われます。)
使用に関しては、次の点に注意してください。
スタンバイ・データベースでのブックマークの位置は、アクティブ・データベースでのブックマークの位置に非常に近くなります。ただし、確認処理のレプリケーションは非同期に行われるため、確認処理の実行頻度によっては、フェイルオーバーの発生時に更新の重複を示す小さなウィンドウが表示されることがあります。
データベースがスタンバイ・ステータスからアクティブ・ステータスに変わった後に、ttXlaClose
関数とttXlaPersistOpen
関数を使用して、そのデータベース上のすべてのブックマークを閉じ、再度開く必要があります。ブックマークは、レプリケーション・エージェントによってスタンバイ・データベース上で必要に応じて自動的に再配置されるため、スタンバイ・データベース上のレプリケートされたブックマークの状態は、通常のXLA処理中に変化します。データベースがアクティブ・ステータスに変更される前に開いていたブックマークを使用しようとすると、ブックマークの状態がリセットされたこととブックマークが再配置されたことを示すエラーが表示されます。この場合でも再配置されたブックマークから読取りを続行できますが、ブックマークを閉じて再度開くと、エラーを回避できます。
レプリケートされたブックマークが存在する間に、アクティブ・スタンバイ・ペアのスキームを削除できます。ブックマークは当然その時点でレプリケートされなくなりますが、削除はされません。続けてアクティブ・スタンバイ・ペア・スキームを再び有効にすると、これらのブックマークは自動的にスキームに追加されます。
レプリケーション・エージェントの実行中は、レプリケートされたブックマークを削除できません。
アクティブ・データベース内のレプリケートされたブックマークのみを読み取って確認できます。レプリケートされたブックマークを確認するたびに、確認処理がスタンバイ・データベースに非同期にレプリケートされます。
XLAが使用中の場合、TimesTenのトランザクション・ログ・ファイルは、XLAのブックマークが進むまで保留されることに注意してください。保留によって、XLAで必要でないことが確認されるまで、トランザクション・ログ・ファイルはパージされません。XLAアプリケーションが予期せず終了したり、そのブックマークの削除や変更トラッキングの無効化を行わずに切断した場合などに、ブックマークが固まってしまうと、ログは保持されたままになり、トランザクション・ログ・ファイルが過度に蓄積される場合があります。この蓄積の結果、ディスク領域が一杯になる場合があります。
この状況を監視し、対処するための情報は、『Oracle TimesTen In-Memory Databaseオペレーション・ガイド』のトランザクション・ログ・ファイルの蓄積の監視に関する説明を参照してください。
表5-1に、内部SQLデータ型と、リリース7.0より前およびリリース7.0以上のXLAデータ型のマッピングを示します。TimesTenデータ型の詳細は、『Oracle TimesTen In-Memory Database SQLリファレンス・ガイド』のデータ型に関する項を参照してください。
表5-1 XLAデータ型のマッピング
内部SQLデータ型 | リリース7.0より前のXLAデータ型 | リリース7.0以上のXLAデータ型 |
---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
- |
|
|
- |
|
|
- |
|
|
- |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- |
|
|
- |
|
|
- |
|
|
|
|
|
|
|
|
|
|
|
- |
|
|
- |
|
|
|
|
|
|
|
|
- |
|
|
- |
|
|
- |
|
|
- |
|
XLAでは、内部SQLデータ型と外部プログラム・データ型を変換する関数が提供されます。たとえば、ttXlaNumberToCString
を使用すると、NUMBER
列を文字列に変換できます。TimesTenは、次のXLAデータ型変換関数を備えています。
データベース内の処理に与えるTimesTenアクセス制御の影響の概要については、「アクセス制御に関するTimesTen機能の考慮事項」を参照してください。アクセス制御は、次のようにXLAに影響を与えます。
次のようなXLA機能の実行には、システム権限XLA
が必要です。
ttXlaPersistOpen
C関数を使用するなど、XLAリーダーとしてのTimesTenへの接続(CREATE SESSION
権限も必要)
その他のXLA関連のTimesTen C関数の実行(第9章「XLAリファレンス」を参照)
XLA関連のTimesTen組込みプロシージャの実行
プロシージャttXlaBookmarkCreate
、ttXlaBookmarkDelete
、ttXlaSubscribe
およびttXlaUnsubscribe
については、『Oracle TimesTen In-Memory Databaseリファレンス』の組込みプロシージャに関する説明を参照してください。
XLA
権限を持つユーザーには、SELECT ANY TABLE
、SELECT ANY VIEW
およびSELECT ANY SEQUENCE
システム権限と同等の権限があり、データベースで発生するDDL文レコードを取得できます。その結果、このユーザーは、XLA権限を持たない場合にはアクセスが許可されていないデータベース・オブジェクトに関する情報を取得できる点に注意してください。
TimesTen XLAの使用時は、次の制限事項に注意してください。
XLAは、TimesTenでサポートされているすべてのプラットフォームで使用できます。ただし、異なるプラットフォーム間、または同じプラットフォームの32ビットと64ビットとの間でのデータの転送はXLAではサポートされていません。
LOBに対するXLAのサポートは限定されています。詳細は、「更新を監視する表の指定」を参照してください。
XLAでは、ドライバ・マネージャ・ライブラリまたはクライアント/サーバー・ライブラリにリンクされたアプリケーションはサポートされていません。(クイック・スタート・アプリケーションで提供されるTimesTenドライバ・マネージャではXLAをサポートしていますが、ドライバ・マネージャ自体が完全にサポートされているわけではありません。「ODBCドライバ・マネージャとのリンク」のこのドライバ・マネージャについての注意を参照してください。)
XLAリーダーは、インメモリー列圧縮を使用する表をサブスクライブできません。
Oracle Databaseに対する変更トラッキング・トリガーでは、自動リフレッシュ・キャッシュ・グループの列レベルでの解決は行いません。(これには非常に費用がかかります。)そのため、自動リフレッシュ機能では、実際にはすべての列のデータが変更されていない場合でも、行のすべての列が更新され、XLAではすべての列が変更されたということのみがレポートされます。
TimesTenには、この章で説明する多数のXLA関数の使用方法を示すxlaSimple
デモが用意されています。このデモはquickstart/sample_code/odbc/xla
ディレクトリにあります。
C開発者向けのTimesTenデモ・プログラムの概要は、「TimesTen Cのデモについて」を参照してください。詳細は、install_dir
/quickstart.html
を参照してください。odbc
ディレクトリのREADMEファイルには、xlaSimple
のビルドおよび実行の手順が記載されています。
次の項「XLAイベント・ハンドラ・アプリケーションの作成」に示すサンプル・コードなど、この章の大部分の内容は、このxlaSimple
デモに基づいています。このデモでは、表MYDATA
がAPPUSER
スキーマに作成されています。APPUSER
としてログインしている間は、その表を更新することになります。XLAUSER
としてログインしている間は、xlaSimple
デモが更新についてレポートします。
デモを実行するには、コマンド・プロンプトでxlaSimple
を実行します。サンプル・データベースの作成時に指定したXLAUSER
のパスワードを入力するように求められます。別のコマンド・プロンプトでttIsql
を開始して、APPUSER
としてTimesTenサンプル・データベースに接続します。再度、サンプル・データベースの作成時に指定したパスワードの入力を求められます。
ttIsql
コマンド・プロンプトで、DML文を入力して表を変更できます。その後、xlaSimple
ウィンドウでXLA出力を表示できます。
この項では、データベース内の選択した表への変更を検出およびレポートするXLAアプリケーションの一般的な作成手順について説明します。「列データの確認」のような例外もありますが、この項で説明する手順は、ほぼすべてのXLAアプリケーションに適用できます。
この項で説明する手順は次のとおりです。
この項のコード例は、xlaSimple
デモ・アプリケーションに基づいています。
ここで説明するXLA関数については、第9章「XLAリファレンス」を参照してください。
すべてのODBCアプリケーションと同様に、XLAアプリケーションは、ODBCを初期化し、環境ハンドル(henv
)を取得し、接続ハンドル(hdbc
)を取得して、特定のデータベースと通信する必要があります。
環境ハンドルおよび接続ハンドルを初期化します。
SQLHENV henv = SQL_NULL_HENV; SQLHDBC hdbc = SQL_NULL_HDBC;
henv
のアドレスをSQLAllocEnv
ODBC関数に渡して環境ハンドルを割り当てます。
rc = SQLAllocEnv(&henv);
hdbc
のアドレスをSQLAllocConnect
ODBC関数に渡して、データベース用の接続ハンドルを割り当てます。
rc = SQLAllocConnect(henv, &hdbc);
SQLDriverConnect
ODBC関数をコールして、接続文字列(connStr
)で指定されたデータベースに接続しますが、この接続文字列は、この例ではコマンドラインから渡されます。
static char connstr[CONN_STR_LEN]; ... rc = SQLDriverConnect(hdbc, NULL, (SQLCHAR*)connstr, SQL_NTS, NULL, 0, NULL, SQL_DRIVER_COMPLETE);
注意: XLAアプリケーションで使用するためにODBC接続ハンドルをオープンすると、ttXlaClose をコールして対応するXLAハンドルをクローズするまで、ODBCハンドルをODBC処理に使用できません。 |
SQLSetConnectOption
ODBC関数をコールして、自動コミットをオフにします。
rc = SQLSetConnectOption(hdbc, SQL_AUTOCOMMIT, SQL_AUTOCOMMIT_OFF);
前述の項「データベース接続ハンドルの取得」の説明に従ってODBCを初期化し、環境ハンドルおよび接続ハンドルを取得した後、XLAを初期化し、XLAハンドルを取得して、トランザクション・ログにアクセスできます。1つのODBC接続に対して1つのみのXLAハンドルを作成します。アプリケーションで複数のXLAリーダー・スレッドを使用する(それぞれが独自のXLAブックマークに接続されている)場合は、スレッドごとに個別のXLAハンドルおよびODBC接続を作成します。
この項では、XLAを初期化する方法について説明します。XLAを初期化する前に、ブックマークを初期化します。その後、XLAハンドルをttXlaHandle_h
型として初期化します。
unsigned char bookmarkName [32]; ... strcpy((char*)bookmarkName, "xlaSimple"); ... ttXlaHandle_h xla_handle = NULL;
bookmarkName
およびxla_handle
のアドレスをttXlaPersistOpen
関数に渡してXLAハンドルを取得します。
rc = ttXlaPersistOpen(hdbc, bookmarkName, XLACREAT, &xla_handle);
XLACREAT
オプションは、新しいレプリケートされていないブックマークの作成に使用します。または、XLAREPL
オプションを使用して、レプリケートされたブックマークを作成します。どちらの場合も、ブックマークがすでに存在する場合は処理に失敗します。
すでに存在するブックマークを使用するには、次の例で示すように、XLAREUSE
オプションを指定してttXlaPersistOpen
をコールします。
#include <tt_errCode.h> /* TimesTen Native Error codes */ ... if ( native_error == 907 ) { /* tt_ErrKeyExists */ rc = ttXlaPersistOpen(hdbc, bookmarkName, XLAREUSE, &xla_handle); ... }
ttXlaPersistOpen
に無効なパラメータが指定されている場合、またはアプリケーションでハンドル用のメモリーを割り当てることができなかった場合は、SQL_INVALID_HANDLE
というコードが返されます。この場合、このエラーまたはこれ以降のエラーの検出にttXlaError
は使用できません。
ttXlaPersistOpen
が失敗しても、まだハンドルを作成する場合は、メモリー・リークを防ぐためにハンドルを閉じる必要があります。
前述の項「XLAの初期化およびXLAハンドルの取得」の説明に従ってXLAを初期化し、XLAハンドルを取得した後、更新イベントを監視する表またはマテリアライズド・ビューを指定できます。
SYS.XLASUBSCRIPTIONS表を問い合せることによって、ブックマークをサブスクライブする表を確認できます。また、
SYS.XLASUBSCRIPTIONS
を使用すると、特定の表にサブスクライブしたブックマークを確認することもできます。
ttXlaNextUpdate
およびttXlaNextUpdateWait
関数は、DDLイベントに関連するXLAレコードを取得します。DDL XLAレコードはどのXLAブックマークでも使用できます。DDLイベントには、CREATAB
、DROPTAB
、CREAIND
、DROPIND
、CREATVIEW
、DROPVIEW
、CREATSEQ
、DROPSEQ
、CREATSYN
、DROPSYN
、ADDCOLS
、DRPCOLS
およびTRUNCATE
トランザクションが含まれています。これらのイベント・タイプの詳細は、「ttXlaUpdateDesc_t」を参照してください。
ttXlaTableStatus
関数は、現行のブックマークを指定した表に更新するためにサブスクライブします。または、現在のブックマークが表に関連するDMLレコードをすでに監視しているかどうかを判別します。
ttXlaTableByName
関数をコールして、指定した表またはマテリアライズド・ビューのシステム識別子およびユーザー識別子の両方を取得します。次に、ttXlaTableStatus
関数をコールして、表またはマテリアライズド・ビューへの変更を監視するためにXLAを有効にします。
XLAでのLOBのサポートは、次のように制限されています。
|
例5-2 更新を監視する表の指定
この例では、MYDATA
表への変更を追跡します。
#define TABLE_OWNER "APPUSER" #define TABLE_NAME "MYDATA" ... SQLUBIGINT SYSTEM_TABLE_ID = 0; ... SQLUBIGINT userID; rc = ttXlaTableByName(xla_handle, TABLE_OWNER, TABLE_NAME, &SYSTEM_TABLE_ID, &userID);
表の識別子を取得すると、ttXlaTableStatus
関数を使用して、MYDATA
表への変更を検出するためのXLA更新追跡を有効にできます。newstatus
パラメータを0(ゼロ)以外の値に設定すると、指定した表に行われた変更がXLAによって追跡されます。
SQLINTEGER oldstatus; SQLINTEGER newstatus = 1; ... rc = ttXlaTableStatus(xla_handle, SYSTEM_TABLE_ID, 0, &oldstatus, &newstatus);
oldstatus
パラメータは、コール時の表のステータスを示す出力です。
ttXlaTableStatus
でnewstatus
をNULLにし、oldstatus
のみを返すことによって、表の現行のXLAステータスを返すことができます。次に例を示します。
rc = ttXlaTableStatus(xla_handle, SYSTEM_TABLE_ID, 0, &oldstatus, NULL); ... if (oldstatus != 0) printf("XLA is currently tracking changes to table %s.%s\n", TABLE_OWNER, TABLE_NAME); else printf("XLA is not tracking changes to table %s.%s\n", TABLE_OWNER, TABLE_NAME);
更新を監視する表を指定した後、ttXlaNextUpdate
またはttXlaNextUpdateWait
関数をコールして、トランザクション・ログから一連のレコードを返すことができます。コミット済のトランザクションのレコードのみが返されます。レコードはコミットされた順序で返されます。不要になり、トランザクション・ログからパージ可能なレコードをXLAが判別できるように、ttXlaAcknowledge
関数を定期的にコールして、トランザクションの受信を確認する必要があります。これらの関数は、トランザクション・ログ内のアプリケーションのブックマークの位置に影響を与えます(「ブックマークの動作」を参照)。詳細は、『Oracle TimesTen In-Memory Databaseリファレンス』のttLogHoldsに関する説明も参照してください。TimesTen組込みプロシージャは、トランザクション・ログの保留に関する情報を返します。
注意: ttXlaAcknowledge は、必要な場合にのみ使用する必要がある高コストの処理です。 |
ttXlaNextUpdate
によって返されたトランザクション内の各更新レコードは、ttXlaUpdateDesc_t
構造体で記述される更新ヘッダーで始まります。この更新ヘッダーには、レコードがトランザクションの最初のレコード(TT_UPDFIRST
)か、最後のコミット・レコード(TT_UPDCOMMIT
)かを示すフラグが格納されています。また、更新ヘッダーは、更新によって影響を受ける表も識別します。更新ヘッダーの後には、データベース内の表に対して行われた変更について記述する0(ゼロ)から2行の行データが続きます。
次の図5-5に、4つの更新レコードで構成されているトランザクションをトランザクション・ログから返すttXlaNextUpdate
へのコールを示します。返されたトランザクションの受信は、ttXlaAcknowledge
をコールすることで確認され、ブックマークが再設定されます。
注意: この例は、説明を明確にするために簡略化されています。実際のXLAアプリケーションでは、ttXlaAcknowledge をコールする前に、複数のトランザクションのレコードを読み取る可能性があります。 |
例5-3 トランザクション・ログからの更新レコードの取得
xlaSimple
デモでは、表の更新に対する監視は、ユーザーが停止するまで続行されます。
この例では、ttXlaNextUpdateWait
をコールする前に、返されるttXlaUpdateDesc_t
レコードを保持するバッファへのポインタ(arry
)と実際に返されるレコード数を保持する変数(records
)を初期化します。この例ではttXlaNextUpdateWait
をコールするため、トランザクション・ログ・バッファでレコードが見つからない場合に待機する秒数(FETCH_WAIT_SECS
)も指定します。
次に、ttXlaNextUpdateWait
をコールし、これらの値を渡してarry
内の一連のttXlaUpdateDesc_t
レコードを取得します。例5-4
に示すように、arry
内の各レコードは、HandleChange()関数に渡すことによって処理されます。すべてのレコードの処理後、ttXlaAcknowledge
をコールして、ブックマークの位置を再設定します。
#define FETCH_WAIT_SECS 5 ... SQLINTEGER records; ttXlaUpdateDesc_t** arry; int j; while (!StopRequested()) { /* Get a batch of update records */ rc = ttXlaNextUpdateWait(xla_handle, &arry, 100, &records, FETCH_WAIT_SECS); if (rc != SQL_SUCCESS { /* See "Handling XLA errors" */ } /* Process the records */ for(j=0; j < records; j++){ ttXlaUpdateDesc_t* p; p = arry[j]; HandleChange(p); /* Described in the next section */ } /* After each batch, Acknowledge updates to reset bookmark.*/ rc = ttXlaAcknowledge(xla_handle); if (rc != SQL_SUCCESS { /* See "Handling XLA errors" */ } } /* end while !StopRequested() */
ttXlaNextUpdate
またはttXlaNextUpdateWait
によって返されるレコードの実際の数は、これらの関数のnreturned
出力パラメータで示されるように、maxrecords
パラメータの値より小さい場合があります。図5-6に示す例では、maxrecords
が10で、7つのレコードで構成されているトランザクションAT
および3つのレコードで構成されているトランザクションBT
がトランザクション・ログに含まれています。この場合、両方のトランザクションが同じバッチで返され、maxrecords
およびnreturned
の両方の値が10になります。ただし、ログ内のその次の3つのトランザクションは、11個のレコードで構成されているCT
、2つのレコードで構成されているDT
および2つのレコードで構成されているET
になります。CT
のコミット・レコードの前にDT
トランザクションのコミット・レコードが書き込まれるため、次にttXlaNextUpdate
をコールすると、DT
トランザクションの2つのレコードが返され、nreturned
の値は2になります。その次にttXlaNextUpdate
をコールすると、XLAによって、CT
トランザクションの合計レコード数がmaxrecords
を超えていることが検出され、このトランザクションのレコードが2つのバッチで返されます。最初のバッチには、CT
の最初の10個のレコード(nreturned
= 10)が含まれます。2番目のバッチには、CT
の最後のレコードおよびET
トランザクションの2つのレコード(ET
の後のトランザクションのコミット・レコードが次の7つのレコード内で検出されないと想定)が含まれます。
これらの関数のパラメータの詳細は、「ttXlaNextUpdate」および「ttXlaNextUpdateWait」を参照してください。
XLAは、レコードをメモリー・バッファまたはディスク上のトランザクション・ログ・ファイルから読み取ります(「XLAでレコードをトランザクション・ログから読み取る方法」を参照)。待機時間を最小にするために、メモリー・バッファからのレコードは使用可能になるとすぐに返されますが、バッファにないレコードはバッファが空の場合にのみ返されます。この設計によって、XLAアプリケーションでは、行われた変更を最小の待機時間ですぐに表示できます。トレードオフとして、ttXlaNextUpdate
またはttXlaNextUpdateWait
のmaxrecords
パラメータでリクエストした数より少ない数の変更が返される場合があります。
注意: スループットを最適化するために、XLAアプリケーションでは、フェッチおよびレコード処理の手順を非同期にする必要があります。たとえば、1つのスレッドを作成してレコードのフェッチおよび保存を行ったり、1つ以上のスレッドを作成して保存したレコードを処理することができます。 |
更新レコードの配列が存在し、その各レコードが示す処理のタイプがわかるため、返された行データを確認できます。
ttXlaNextUpdate
またはttXlaNextUpdateWait
関数によって返される各レコードは、ttXlaUpdateDesc_t
ヘッダーで始まり、このヘッダーには次の情報が記述されています。
処理が実行された表
レコードがトランザクションの最初のレコードであるか、または最後の(コミット)レコードであるか
レコードが示す処理のタイプ
返された行データの長さ(行データがある場合)
行内の更新された列(該当する列がある場合)
図5-7に、トランザクション・ログ内の更新レコードの例を示します。
ttXlaUpdateDesc_t
ヘッダーは固定長で、処理のタイプに応じて、データベースから0(ゼロ)から2行の行(タプル)が続きます。ttXlaUpdateDesc_t
ヘッダーのアドレスを取得し、それにsizeof(ttXlaUpdateDesc_t)
を追加することによって、最初に返された行のアドレスを特定できます。
tup1 = (void*) ((char*) ttXlaUpdateDesc_t + sizeof(ttXlaUpdateDesc_t));
これを次の例5-4に示します。
ttXlaUpdateDesc_t ->
type
フィールドは、更新を生成したSQL処理のタイプについての記述です。UPDATETTUP
型のトランザクション・レコードは、UPDATE
処理についての記述であるため、更新の前後の行データをレポートするために2行の行を返します。更新後の値が含まれている2番目に返された行のアドレスは、レコード内の1つ目の行のアドレスにその長さに追加することで特定できます。
if (ttXlaUpdateDesc_t->type == UPDATETUP) { tup2 = (void*) ((char*) tup1 + ttXlaUpdateDesc_t->tuple1); }
これも例5-4に示します。
例5-4 レコード・ヘッダーでのSQL処理タイプの確認
この例では、ttXlaNextUpdateWait
関数によって返された各レコードをHandleChange()
関数に渡し、この関数では、そのレコードがINSERT
、UPDATE
またはCREATE VIEW
処理に関連しているかを判別します。この例では、わかりやすくするために、その他の処理はすべて無視されています。
HandleChange()
関数は、PrintColValues()
関数(例5-13を参照)をコールする前に各タイプのSQL処理を異なる方法で処理します。
void HandleChange(ttXlaUpdateDesc_t* xlaP) { void* tup1; void* tup2; /* First confirm that the XLA update is for the table we care about. */ if (xlaP->sysTableID != SYSTEM_TABLE_ID) return ; /* OK, it's for the table we're monitoring. */ /* The last record in the ttXlaUpdateDesc_t record is the "tuple2" * field. Immediately following this field is the first XLA record "row". */ tup1 = (void*) ((char*) xlaP + sizeof(ttXlaUpdateDesc_t)); switch(xlaP->type) { case INSERTTUP: printf("Inserted new row:\n"); PrintColValues(tup1); break; case UPDATETUP: /* If this is an update ttXlaUpdateDesc_t, then following that is * the second XLA record "row". */ tup2 = (void*) ((char*) tup1 + xlaP->tuple1); printf("Updated row:\n"); PrintColValues(tup1); printf("To:\n"); PrintColValues(tup2); break; case DELETETUP: printf("Deleted row:\n"); PrintColValues(tup1); break; default: /* Ignore any XLA records that are not for inserts/update/delete SQL ops. */ break; } /* switch (xlaP->type) */ }
更新レコードでは、0(ゼロ)から2行のデータがttXlaUpdateDesc_t構造体の後に返される場合があります(「レコード・ヘッダーの確認および行アドレスの検出」
を参照)。図5-8に示すように、各行のデータの最初の部分は固定長で、その後に可変長データが続きます。
列データを確認する手順は次のとおりです。
返された行から列値を読み取るには、まず、その行の各列のオフセットを判別する必要があります。列オフセットおよびその他の列メタデータは、特定の表に対してttXlaGetColumnInfo
関数をコールすると取得でき、この関数は表の列ごとに個別のttXlaColDesc_t
構造体を返します。ttXlaGetColumnInfo
関数は、初期化プロシージャの一部としてコールする必要があります。「XLAの初期化およびXLAハンドルの取得」の説明では、わかりやすくするためにこのコールは省略されています。
ttXlaGetColumnInfo
をコールする場合は、colinfo
パラメータを指定して、返されるttXlaColDesc_t
構造体のリストを保持するバッファへのポインタを作成します。maxcols
パラメータを使用して、バッファのサイズを定義します。
例5-5 列記述の使用
次に示すxlaSimple
デモのコード例では、返される列の最大数(MAX_XLA_COLUMNS
)を推測し、返されるttXlaColDesc_t
構造体を保持するバッファ(xla_column_defs
)のサイズを設定します。maxcols
パラメータを設定する場合のもう1つのより正確な方法として、ttXlaGetTableInfo
関数をコールし、ttXlaColDesc_t ->
columns
に返された値を使用する方法があります。
#define MAX_XLA_COLUMNS 128 ... SQLINTEGER ncols; ... ttXlaColDesc_t xla_column_defs[MAX_XLA_COLUMNS]; ... rc = ttXlaGetColumnInfo(xla_handle, SYSTEM_TABLE_ID, userID, xla_column_defs, MAX_XLA_COLUMNS, &ncols); if (rc != SQL_SUCCESS { /* See "Handling XLA errors" */ }
図5-9に示すように、ttXlaGetColumnInfo
関数は次の情報を出力します。
ttXlaGetColumnInfo
colinfo
パラメータが指すバッファに格納される
ttXlaColDesc_t構造体のリスト(xla_column_defs
)
xla_column_defsバッファに実際に返される列数を保持する
nreturnedの値(
ncols
)
図5-9 ttXlaGetColumnInfoによって返されるttXlaColDesc_t構造体
ttXlaGetColumnInfo
によって返される各ttXlaColDesc_t
構造体には、その列のオフセット位置を記述するoffset値が含まれます。列データを読み込む際にこのoffset値をどのように使用するかは、列が固定長データ(CHAR
、NCHAR
、INTEGER
、BINARY
、DOUBLE
、FLOAT
、DATE
、TIME
、TIMESTAMP
など)を含んでいるか、可変長データ(VARCHAR
、NVARCHAR
、VARBINARY
など)を含んでいるかによって異なります。
固定長列データの場合、列のアドレスは、ttXlaColDesc_t
構造体のoffset値に行のアドレスを追加したものです。
例5-6 固定長列データの読取り
ここに示すような計算の完全な動作例については、例5-13を参照してください。
MYDATA
表の最初の列はCHAR
型です。HandleChange()
関数(例5-4
)で前に取得したtup1行のアドレスと、ttXlaGetColumnInfo
関数(例5-5
)によって返された最初のttXlaColDesc_t構造体のoffsetを使用する場合は、次のように計算して最初の列の値を取得できます。
char* Column1; Column1 = ((unsigned char*) tup1 + xla_column_defs[0].offset);
MYDATA
表の3番目の列はINTEGER
型のため、3番目のttXlaColDesc_t
構造体のoffsetを使用して値を検出し、次のように計算して、整数として再キャストできます。データは適切に配置されます。
int Column3; Column3 = *((int*) ((unsigned char*) tup + xla_column_defs[2].offset));
MYDATA
表の4番目の列はNCHAR
型のため、4番目のttXlaColDesc_t
構造体のoffsetを使用して値を検出し、次のように計算して、SQLWCHAR
型として再キャストできます。
SQLWCHAR* Column4; Column4 = (SQLWCHAR*) ((unsigned char*) tup + xla_column_defs[3].offset);
前述の例で取得した列値とは異なり、Column4
は2バイトのUnicode文字の配列を指します。文字列を取得するには、例5-13
のSQL_WCHARの場合のように、この配列内の各要素に対して繰り返し実行する必要があります。
その他の固定長データ型は、対応するCの型にキャストできます。複合固定長データ型(DATE
、TIME
、DECIMAL
値など)は、TimesTenの内部形式で格納されますが、アプリケーションでXLA変換関数を使用して、対応するODBC C値に変換できます(「複合データ型の変換」を参照)。
NOT INLINE
可変長データ(VARCHAR
、NVARCHAR
およびVARBINARY
)の場合、ttXlaColDesc_t ->
offset
にあるデータは、返された行の可変長部分でのデータの場所を指す4バイトのオフセット値です。オフセット・アドレスをオフセット値に追加すると、行の可変長部分の列データのアドレスを取得できます。この場所の最初のn
バイトはデータの長さで、その後に実際のデータが続きます(n
は、32ビットのプラットフォームでは4、64ビットのプラットフォームでは8です)。可変長データの場合、ttXlaColDesc_t ->
size
値は列の許容最大サイズです。図5-11に、行内のNOT INLINE
可変長データを検出する方法を示します。
例5-7 NOT INLINE可変長列データの読取り
ここに示すような計算の完全な動作例については、例5-13「完全なPrintColValues()関数」を参照してください。
引き続きこの例では、返された行(tup1
)の2番目の列はVARCHAR
型です。行内の可変長データを検出するには、まず前述の図5-11
に示すように、行の固定長部分で列のttXlaColDesc_t ->
offsetの値を検出します。このアドレスの値は、行の可変長部分のデータの4バイトのオフセットです(VarOffset
)。次に、VarOffset
のアドレスにVarOffset
オフセット値を追加して、可変長列データの先頭へのポインタを取得します(DataLength
)。32ビット・プラットフォームで処理が行われると想定すると、DataLength
位置の最初の4バイトがデータの長さです。DataLength
の後の次のバイトが、実際のデータの先頭です(Column2
)。
このコード例では、32ビット・プラットフォームで処理が行われると想定しているため、DataLength
は32ビット・タイプとして初期化されます。64ビット・プラットフォームでは、DataLength
を64ビット・タイプとして初期化する必要があり、Column2
データはオフセット・アドレスDataLength
の後に64ビット+1で書き込まれます。
void* VarOffset; /* offset of data */ long* DataLength; /* length of data */ char* Column2; /* pointer to data */ VarOffset = (void*) ((unsigned char*) tup1 + xla_column_defs[1].offset); /* * If column is out-of-line, pColVal points to an offset * else column is inline so pColVal points directly to the string length. */ if (xla_column_defs[1].flags & TT_COLOUTOFLINE) DataLength = (long*)((char*)VarOffset + *((int*)VarOffset)); else DataLength = (long*)VarOffset; Column2 = (char*)(DataLength+1);
VARBINARY
型は、VARCHAR
型での方法とほぼ同じ方法で処理されます。Column2
がNVARCHAR
型の場合、SQLWCHAR
として初期化し、前述のVARCHAR
の場合と同様に値を取得し、次に例5-13
のNCHAR
値CharBuf
の場合と同様にColumn2配列に対して繰り返し実行できます。
注意: 前述の例では、DataLength はデータ型long で、64-bitのシステムでは64ビット(8バイト)型で、32-bitのシステムでは32ビット(4バイト)型として記載されています。ほとんどのUNIXシステムに当てはまりますが、Windowsの64-bitシステムのlong は4バイト型です。 |
レコードの行データから返される文字列は空文字では終了していません。文字列をバッファにコピーし、その文字列の最後の文字の後に\0
などの空文字を追加すると、文字列を空文字で終了できます。
文字列を空文字で終了する手順は、文字列が固定長の場合と可変長の場合で少し異なります。例5-8に、固定長文字列を空文字で終了する手順を示します。その後の例5-9には、サイズがわかっている可変長文字列を空文字で終了する手順を示します。例5-10には、サイズがわかっていない文字列の場合の手順を示します。
例5-8 固定長文字列の空文字での終了
ここに示すような計算の完全な動作例については、例5-13を参照してください。
例5-6
で返された固定長CHAR(10)
Column1文字列を空文字で終了するには、文字列と空文字を保持できる十分な大きさのバッファを設定します。次に、ttXlaColDesc_t -
>size
から文字列のサイズを取得し、文字列をバッファにコピーして、次のような計算で文字列の末尾を空文字で終了します。これで、バッファの内容を使用できます。この例では、その文字列を出力します。
char buffer[10+1]; int size; size = xla_column_defs[0].size; memcpy(buffer, Column1, size); buffer[size] = '\0'; printf(" Row %s is %s\n", ((unsigned char*) xla_column_defs[0].colName), buffer);
可変長文字列を空文字で終了する手順は、固定長文字列の手順とほぼ同じです。可変長データのオフセットの先頭にある値は、文字列のサイズのみです(「NOT INLINE可変長列データの読取り」を参照)。
例5-9 サイズがわかっている可変長文字列の空文字での終了
(ここに示すような計算の完全な動作例については、例5-13を参照してください。)
例5-7
で取得したColumn2文字列がVARCHAR(32)
の場合、文字列と空文字を保持できる十分な大きさのバッファを設定します。DataLength
オフセットにある値を使用して、次のような計算で文字列のサイズを設定します。
char buffer[32+1]; memcpy(buffer, Column2, *DataLength); buffer[*DataLength] = '\0'; printf(" Row %s is %s\n", ((unsigned char*) xla_column_defs[1].colName), buffer);
すべてのデータ型を読み取るための汎用コードを記述する場合は、返される文字列のサイズを想定できません。サイズがわからない文字列に対しては、返された文字列の大部分を保持できる十分な大きさのバッファを静的に割り当てます。返された文字列がバッファより大きい場合は、例5-10に示すように、正しいサイズのバッファを動的に割り当てます。
例5-10 サイズがわかっていない可変長文字列の空文字での終了
例5-7
で取得したColumn2文字列のサイズがわからない場合は、最大10000文字の文字列を保持できる十分な大きさのバッファを静的に割り当てます。その後、可変長データ・オフセットの先頭で取得したDataLength
値が、バッファのサイズより小さいことを確認します。文字列がバッファより大きい場合は、malloc()
を使用してバッファを動的に正しいサイズに割り当てます。
#define STACKBUFSIZE 10000 char VarStackBuf[STACKBUFSIZE]; char* buffer; buffer = (*DataLength+1 <= STACKBUFSIZE) ? VarStackBuf : malloc(*DataLength+1); memcpy(buffer,Column2,*DataLength); buffer[*DataLength] = '\0'; printf(" Row %s is %s\n", ((unsigned char*) xla_column_defs[1].colName), buffer); if (buffer != VarStackBuf) /* buffer was allocated */ free(buffer);
TT_DATE
、TT_TIME
、TT_DECIMAL
などの複合データ型の値は、TimesTenの内部形式で保存され、これらはXLA型変換関数を使用して、対応するODBC C型に変換できます。表5-2に、これらの変換関数の説明を示します。
表5-2 XLAデータ型変換関数
関数 | 変換 |
---|---|
内部 |
|
内部 |
|
内部 |
|
内部 |
|
内部 |
|
内部 |
|
内部 |
|
内部 |
|
内部 |
|
内部 |
|
内部 |
|
内部 |
|
内部 |
|
内部 |
|
内部 |
|
内部 |
|
内部 |
これらの変換関数は、ttXlaUpdateDesc_t
型(UPDATETUP
、INSERTTUP
およびDELETETUP
)に含まれる行データで使用できます。
例5-11 複合データ型の変換
(ここに示すような計算の完全な動作例については、例5-13を参照してください。)
前にHandleChange()
関数(例5-4
)で取得したtup1行のアドレス、およびttXlaGetColumnInfo
関数(例5-5)によって返された5番目の
ttXlaColDesc_t構造体のoffsetを使用すると、TIMESTAMP
型の列値を検出できます。ttXlaTimeStampToODBCCType
関数を使用し、TimesTen形式から列データを変換し、変換されたTIME値をODBC TIMESTAMP_STRUCT
に保存します。次のようなコードを使用すると値を出力できます。
void* Column5; TIMESTAMP_STRUCT timestamp; Column5 = (void*) ((unsigned char*) tup1 + xla_column_defs[4].offset); rc = ttXlaTimeStampToODBCCType(Column5, ×tamp); if (rc != SQL_SUCCESS) { /* See "Handling XLA errors" */ } printf(" %s: %04d-%02d-%02d %02d:%02d:%02d.%06d\n", ((unsigned char*) xla_column_defs[i].colName), timestamp.year,timestamp.month, timestamp.day, timestamp.hour,timestamp.minute,timestamp.second, timestamp.fraction);
前にHandleChange()
関数(例5-4
)で取得したtup1行のアドレス、およびttXlaGetColumnInfo
関数(例5-5
)によって返された6番目のttXlaColDesc_t構造体のoffsetを使用すると、DECIMAL
型の列値を検出できます。ttXlaDecimalToCString
関数を使用して、TimesTenのDECIMAL形式から文字列に列データを変換します。次のようなコードを使用すると値を出力できます。
char decimalData[50]; Column6 = (float*) ((unsigned char*) tup + xla_column_defs[5].offset); precision = (short) (xla_column_defs[5].precision); scale = (short) (xla_column_defs[5].scale); rc = ttXlaDecimalToCString(Column6, (char*)&decimalData, precision, scale); if (rc != SQL_SUCCESS) { /* See "Handling XLA errors" */ } printf(" %s: %s\n", ((unsigned char*) xla_column_defs[5].colName), decimalData);
NULL値可能な表の列の場合、ttXlaColDesc_t ->
nullOffset
は、レコードの列のNULLバイトを指します。列にNULLを指定できない場合、このフィールドは0(ゼロ)で、列にNULLを指定できる場合、このフィールドは0より大きくなります。
NULL値可能な列の場合(ttXlaColDesc_t ->
nullOffset
> 0)、列がNULLであるかどうかを確認するには、NULLオフセットをttXlaUpdate_t*
のアドレスに追加し、(unsigned char
)バイトをチェックして1 (NULL
)または0 (NOT NULL
)のいずれであるかを確認します。
例5-13に、各列のttXlaColDesc_t ->
dataType
を確認して、データ型がCHAR
、NCHAR
、INTEGER
、TIMESTAMP
、DECIMAL
およびVARCHAR
である列を検出し、その値を出力する関数を示します。これは、考えられる1つの方法にすぎません。たとえば、別の方法として、ttXlaColDesc_t ->
ColName
の値を確認して、名前で特定の列を検出する方法もあります。
PrintColValues()
関数は、長さが最大50バイトのCHAR
およびVARCHAR
の文字列を処理します。NCHAR
文字は、ASCIIキャラクタ・セットに属している必要があります。
例5-13 完全なPrintColValues()関数
この例では、関数はまずttXlaColDesc_t ->
nullOffset
を調べて列がNULLかどうかを確認します。次に、ttXlaColDesc_t ->
dataType
のフィールドを調べて列のデータ型を確認します。単純な固定長データ(CHAR
、NCHAR
およびINTEGER
)の場合は、ttXlaColDesc_t ->
offset
にある値を適切なCの型にキャストします。複合データ型(TIMESTAMP
およびDECIMAL
)は、ttXlaTimeStampToODBCCType
関数およびttXlaDecimalToCString
関数を使用して、TimesTen形式からODBC C値に変換されます。
可変長データ(VARCHAR
)の場合、関数は行の可変長部分でデータを検出します(「XLAエラーの処理」を参照)。
void PrintColValues(void* tup) { SQLRETURN rc ; SQLINTEGER native_error; void* pColVal; char buffer[50+1]; /* No strings over 50 bytes */ int i; for (i = 0; i < ncols; i++) { if (xla_column_defs[i].nullOffset != 0) { /* See if column is NULL */ /* this means col could be NULL */ if (*((unsigned char*) tup + xla_column_defs[i].nullOffset) == 1) { /* this means that value is SQL NULL */ printf(" %s: NULL\n", ((unsigned char*) xla_column_defs[i].colName)); continue; /* Skip rest and re-loop */ } } /* Fixed-length data types: */ /* For INTEGER, recast as int */ if (xla_column_defs[i].dataType == TTXLA_INTEGER) { printf(" %s: %d\n", ((unsigned char*) xla_column_defs[i].colName), *((int*) ((unsigned char*) tup + xla_column_defs[i].offset))); } /* For CHAR, just get value and null-terminate string */ else if ( xla_column_defs[i].dataType == TTXLA_CHAR_TT || xla_column_defs[i].dataType == TTXLA_CHAR) { pColVal = (void*) ((unsigned char*) tup + xla_column_defs[i].offset); memcpy(buffer, pColVal, xla_column_defs[i].size); buffer[xla_column_defs[i].size] = '\0'; printf(" %s: %s\n", ((unsigned char*) xla_column_defs[i].colName), buffer); } /* For NCHAR, recast as SQLWCHAR. NCHAR strings must be parsed one character at a time */ else if ( xla_column_defs[i].dataType == TTXLA_NCHAR_TT || xla_column_defs[i].dataType == TTXLA_NCHAR ) { SQLUINTEGER j; SQLWCHAR* CharBuf; CharBuf = (SQLWCHAR*) ((unsigned char*) tup + xla_column_defs[i].offset); printf(" %s: ", ((unsigned char*) xla_column_defs[i].colName)); for (j = 0; j < xla_column_defs[i].size / 2; j++) { printf("%c", CharBuf[j]); } printf("\n"); } /* Variable-length data types: For VARCHAR, locate value at its variable-length offset and null-terminate. VARBINARY types are handled in a similar manner. For NVARCHARs, initialize 'var_data' as a SQLWCHAR, get the value as shown below, then iterate through 'var_len' as shown for NCHAR above */ else if ( xla_column_defs[i].dataType == TTXLA_VARCHAR || xla_column_defs[i].dataType == TTXLA_VARCHAR_TT) { long* var_len; char* var_data; pColVal = (void*) ((unsigned char*) tup + xla_column_defs[i].offset); /* * If column is out-of-line, pColVal points to an offset * else column is inline so pColVal points directly to the string length. */ if (xla_column_defs[i].flags & TT_COLOUTOFLINE) var_len = (long*)((char*)pColVal + *((int*)pColVal)); else var_len = (long*)pColVal; var_data = (char*)(var_len+1); memcpy(buffer,var_data,*var_len); buffer[*var_len] = '\0'; printf(" %s: %s\n", ((unsigned char*) xla_column_defs[i].colName), buffer); } /* Complex data types require conversion by the XLA conversion methods Read and convert a TimesTen TIMESTAMP value. DATE and TIME types are handled in a similar manner */ else if ( xla_column_defs[i].dataType == TTXLA_TIMESTAMP || xla_column_defs[i].dataType == TTXLA_TIMESTAMP_TT) { TIMESTAMP_STRUCT timestamp; char* convFunc; pColVal = (void*) ((unsigned char*) tup + xla_column_defs[i].offset); if (xla_column_defs[i].dataType == TTXLA_TIMESTAMP_TT) { rc = ttXlaTimeStampToODBCCType(pColVal, ×tamp); convFunc="ttXlaTimeStampToODBCCType"; } else { rc = ttXlaOraTimeStampToODBCTimeStamp(pColVal, ×tamp); convFunc="ttXlaOraTimeStampToODBCTimeStamp"; } if (rc != SQL_SUCCESS) { handleXLAerror (rc, xla_handle, err_buf, &native_error); fprintf(stderr, "%s() returns an error <%d>: %s", convFunc, rc, err_buf); TerminateGracefully(1); } printf(" %s: %04d-%02d-%02d %02d:%02d:%02d.%06d\n", ((unsigned char*) xla_column_defs[i].colName), timestamp.year,timestamp.month, timestamp.day, timestamp.hour,timestamp.minute,timestamp.second, timestamp.fraction); } /* Read and convert a TimesTen DECIMAL value to a string. */ else if (xla_column_defs[i].dataType == TTXLA_DECIMAL_TT) { char decimalData[50]; short precision, scale; pColVal = (float*) ((unsigned char*) tup + xla_column_defs[i].offset); precision = (short) (xla_column_defs[i].precision); scale = (short) (xla_column_defs[i].scale); rc = ttXlaDecimalToCString(pColVal, (char*)&decimalData, precision, scale); if (rc != SQL_SUCCESS) { handleXLAerror (rc, xla_handle, err_buf, &native_error); fprintf(stderr, "ttXlaDecimalToCString() returns an error <%d>: %s", rc, err_buf); TerminateGracefully(1); } printf(" %s: %s\n", ((unsigned char*) xla_column_defs[i].colName), decimalData); } else if (xla_column_defs[i].dataType == TTXLA_NUMBER) { char numbuf[32]; pColVal = (void*) ((unsigned char*) tup + xla_column_defs[i].offset); rc=ttXlaNumberToCString(xla_handle, pColVal, numbuf, sizeof(numbuf)); if (rc != SQL_SUCCESS) { handleXLAerror (rc, xla_handle, err_buf, &native_error); fprintf(stderr, "ttXlaNumberToDouble() returns an error <%d>: %s", rc, err_buf); TerminateGracefully(1); } printf(" %s: %s\n", ((unsigned char*) xla_column_defs[i].colName), numbuf); } } /* End FOR loop */ }
注意:
|
ODBCまたはXLA関数をコールするたびに、エラーのリターン・コードを確認する必要があります。エラーが致命的である場合は、「XLAアプリケーションの終了」の説明に従ってプログラムを終了します。
エラーは、エラー・コード(エラー番号)またはtt_Err
文字列のいずれかを使用して確認できます。TimesTenエラー・コードとエラー文字列の完全なリストは、install_dir
/include/tt_errCode.h
ファイルを参照してください。各メッセージについては、『Oracle TimesTen In-Memory Databaseエラー・メッセージおよびSNMPトラップ』のエラーおよび警告のリストに関する項を参照してください。
XLA関数のリターン・コードがSQL_SUCCESS
でない場合は、ttXlaError
関数を使用して、XLAハンドルでXLA固有のエラーを取得します。
「エラーのチェック」も参照してください。
例5-14 リターン・コードの確認とエラー処理関数のコール
この例では、XLA関数ttXlaTableByName
をコールした後、リターン・コードがSQL_SUCCESS
であるかどうかを確認します。そうでない場合は、XLAエラー処理関数をコールし、その後アプリケーションを終了する関数をコールします。「XLAアプリケーションの終了」も参照してください。
rc = ttXlaTableByName(xla_handle, TABLE_OWNER, TABLE_NAME, &SYSTEM_TABLE_ID, &userID); if (rc != SQL_SUCCESS) { handleXLAerror (rc, xla_handle, err_buf, &native_error); fprintf(stderr, "ttXlaTableByName() returns an error <%d>: %s", rc, err_buf); TerminateGracefully(1); }
XLAエラー処理関数は、エラー・スタックからすべてのXLAエラーが読み取られまで繰り返しttXlaError
をコールする必要があり、ttXlaError
からのリターン・コードがSQL_NO_DATA_FOUND
になるまで続行します。エラーを再度読み取る必要がある場合は、ttXlaErrorRestart
関数をコールしてエラー・スタックのポインタを最初のエラーに再設定します。
エラー・スタックは、ttXlaError
またはttXlaErrorRestart
以外のすべてのXLA関数へのコール後に消去されます。
注意: ttXlaPersistOpen がXLAハンドルを作成できない場合は、エラー・コードSQL_INVALID_HANDLE を返します。XLAハンドルが作成されていないため、ttXlaError を使用してこのエラーを検出することはできません。SQL_INVALID_HANDLE は、メモリーが割り当てることができない場合または指定したパラメータが無効である場合にのみ返されます。 |
アプリケーションによっては、表5-3に示すような特定のXLAエラーに対処する必要がある場合があります。
表5-3 XLAエラーとコード
エラー | コード |
---|---|
802(一時的) |
|
6001(一時的) |
|
6002(一時的) |
|
6003(一時的) |
|
6220(一時的) |
|
6221(一時的) |
|
8024 |
|
8029 |
|
8031 |
|
8034 |
|
8035 |
|
8036 |
|
8037 |
|
8038 |
|
8046 |
|
8047 |
例5-15 handleXLAerror()関数のコール
この例では、xlaSimple
デモ・プログラムのエラー関数であるhandleXLAerror()
を示します。
void handleXLAerror(SQLRETURN rc, ttXlaHandle_h xlaHandle, SQLCHAR* err_msg, SQLINTEGER* native_error) { SQLINTEGER retLen; SQLINTEGER code; char* err_msg_ptr; /* initialize return codes */ rc = SQL_ERROR; *native_error = -1; err_msg[0] = '\0'; err_msg_ptr = (char*)err_msg; while (1) { int rc = ttXlaError(xlaHandle, &code, err_msg_ptr, ERR_BUF_LEN - (err_msg_ptr - (char*)err_msg), &retLen); if (rc == SQL_NO_DATA_FOUND) { break; } if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { sprintf(err_msg_ptr, "*** Error fetching error message via ttXlaError(); rc=<%d>.",rc) ; break; } rc = SQL_ERROR; *native_error = code ; /* append any other error messages */ err_msg_ptr += retLen; } }
XLAブックマークがサブスクライブされている表を削除するには、ブックマークから表をサブスクライブ解除する必要があります。ブックマークから表をサブスクライブ解除するには、アプリケーションがブックマークに接続されているかどうかに応じて、いくつかの方法があります。
XLAアプリケーションが接続されており、削除する表を追跡しているブックマークを使用している場合は、次のタスクを実行します。
各XLAアプリケーションでttXlaTableStatus
関数をコールし、newstatus
パラメータを0に設定します。これにより、アプリケーションで使用中のXLAブックマークから表がサブスクライブ解除されます。
表を削除します。
XLAアプリケーションが接続されておらず、削除する表に関連付けられているブックマークを使用している場合は、次のタスクを実行します。
SYS.XLASUBSCRIPTIONS
システム表を問い合せて、削除する表にサブスクライブされているブックマークを確認します。
ttXlaUnsubscribe
組込みプロシージャを使用して、表にサブスクライブされている各XLAブックマークから表をサブスクライブ解除します。
表を削除します。
ブックマークを削除すると、XLAブックマークからも表がサブスクライブ解除されます。次の項「ブックマークの削除」を参照してください。
アプリケーションを終了する場合や表を削除する場合に、ブックマークを削除することがあります。アプリケーションが接続されており、ブックマークを使用している場合は、ttXlaDeleteBookmark
関数を使用してXLAブックマークを削除します。
ブックマークは、前の接続がクローズされた後に新しい接続で再利用される可能性があります(「XLAブックマークについて」を参照)。新しい接続は、前の接続が停止した場所からトランザクション・ログの読取りを再開します。次の点に注意してください。
ブックマークを削除すると、後続のチェックポイント処理(組込みプロシージャttCkpt
、ttCkptBlocking
など)によって、トランザクション・ログ内の未読の更新レコードに関連するディスク領域が解放されます。
ブックマークを削除しない場合、XLAアプリケーションは、接続してブックマークを再利用する際に、プログラムの終了以降累積していたすべての未読の更新レコードを読み取ります。これは、更新レコードがTimesTenトランザクション・ログで永続的であるためです。ただし、これらの未読のレコードがトランザクション・ログ・ファイルに累積し、大量のディスク領域を使用すると危険です。
注意:
|
例5-16 ブックマークの削除
次の例に示すように、xlaSimple
デモのInitHandler()
関数は、終了時にXLAブックマークを削除します。
if (deleteBookmark) { ttXlaDeleteBookmark(xla_handle); if (rc != SQL_SUCCESS) { /* See "Handling XLA errors" */ } xla_handle = NULL; /* Deleting the bookmark has the */ /* effect of disconnecting from XLA. */ } /* Close the XLA connection as described in the next section, "Terminating an XLA application". */
アプリケーションが接続されておらず、XLAブックマークを使用している場合、次のいずれかの方法でブックマークを削除できます。
XLAアプリケーションでトランザクション・ログの読取りを終了したら、コミットされていないトランザクションをロールバックし、すべてのハンドルを解放して、正常に終了する必要があります。これには次の2つの方法があります。
すべての表およびマテリアライズド・ビューのサブスクライブを解除し、XLAブックマークを削除し、データベースから切断します。
または
データベースからは切断しますが、XLAブックマークは維持します。後で再接続する際に、ブックマークからレコードの読取りを再開できます。
最初の方法では、次の手順を実行します。
ttXlaTableStatus
をコールして各表およびマテリアライズド・ビューをサブスクライブ解除します(newstatus
パラメータを0(ゼロ)に設定します)。
ttXlaDeleteBookmark
をコールして、ブックマークを削除します。「ブックマークの削除」を参照してください。
ttXlaClose
をコールして、XLAハンドルを切断します。
SQL_ROLLBACK
を指定してODBC関数SQLTransact
をコールし、コミットされていないトランザクションをすべてロールバックします。
ODBC関数SQLDisconnect
をコールして、TimesTenデータベースから切断します。
ODBC関数SQLFreeConnect
をコールして、ODBC接続ハンドルに割り当てられているメモリーを解放します。
ODBC関数SQLFreeEnv
をコールして、ODBC環境ハンドルを解放します。
2番目の方法では、ブックマークを維持するため、最初の2つの手順を省略し残りの手順を完了します。
割当てとは反対の順序でリソースが解放されることに注意してください。たとえば、ODBC環境ハンドルはODBC接続ハンドルより先に割り当てられているため、クリーンアップでは、接続ハンドルを環境ハンドルより先に解放します。
例5-17 XLAアプリケーションの終了
この例では、xlaSimple
クイック・スタート・デモの終了関数であるTerminateGracefully()
を示します。
void TerminateGracefully(int status) { SQLRETURN rc; SQLINTEGER native_error ; SQLINTEGER oldstatus; SQLINTEGER newstatus = 0; /* If the table has been subscribed to through XLA, unsubscribe it. */ if (SYSTEM_TABLE_ID != 0) { rc = ttXlaTableStatus(xla_handle, SYSTEM_TABLE_ID, 0, &oldstatus, &newstatus); if (rc != SQL_SUCCESS) { handleXLAerror (rc, xla_handle, err_buf, &native_error); fprintf(stderr, "Error when unsubscribing from "TABLE_OWNER"."TABLE_NAME " table <%d>: %s", rc, err_buf); } SYSTEM_TABLE_ID = 0; } /* Close the XLA connection. */ if (xla_handle != NULL) { rc = ttXlaClose(xla_handle); if (rc != SQL_SUCCESS) { fprintf(stderr, "Error when disconnecting from XLA:<%d>", rc); } xla_handle = NULL; } if (hstmt != SQL_NULL_HSTMT) { rc = SQLFreeStmt(hstmt, SQL_DROP); if (rc != SQL_SUCCESS) { handleError(rc, henv, hdbc, hstmt, err_buf, &native_error); fprintf(stderr, "Error when freeing statement handle:\n%s\n", err_buf); } hstmt = SQL_NULL_HSTMT; } /* Disconnect from TimesTen entirely. */ if (hdbc != SQL_NULL_HDBC) { rc = SQLTransact(henv, hdbc, SQL_ROLLBACK); if (rc != SQL_SUCCESS) { handleError(rc, henv, hdbc, hstmt, err_buf, &native_error); fprintf(stderr, "Error when rolling back transaction:\n%s\n", err_buf); } rc = SQLDisconnect(hdbc); if (rc != SQL_SUCCESS) { handleError(rc, henv, hdbc, hstmt, err_buf, &native_error); fprintf(stderr, "Error when disconnecting from TimesTen:\n%s\n", err_buf); } rc = SQLFreeConnect(hdbc); if (rc != SQL_SUCCESS) { handleError(rc, henv, hdbc, hstmt, err_buf, &native_error); fprintf(stderr, "Error when freeing connection handle:\n%s\n", err_buf); } hdbc = SQL_NULL_HDBC; } if (henv != SQL_NULL_HENV) { rc = SQLFreeEnv(henv); if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { handleError(rc, henv, hdbc, hstmt, err_buf, &native_error); fprintf(stderr, "Error when freeing environment handle:\n%s\n", err_buf); } henv = SQL_NULL_HENV; } exit(status); }
『Oracle TimesTen In-Memory Database開発者および管理者ガイド』に記載されているとおり、TimesTenのレプリケーションは、ほとんどの顧客要件を満たすには十分ですが、XLA関数を使用して、1つのデータベースから別のデータベースに更新をレプリケートすることもできます。この方法で独自のレプリケーション・スキームをXLAの上に実装することはかなり困難ですが、TimesTenのレプリケーションがなんらかの理由で実行できない場合には検討できます。
注意: 異なるプラットフォーム間または同じプラットフォームの32ビットと64ビットのバージョン間では、XLAを使用して更新をレプリケートできません。 |
この項では、送信側のデータベースをマスター、受信側のデータベースをサブスクライバと呼びます。XLAを使用してデータベース間で変更をレプリケートするには、まずttXlaPersistOpen
関数を使用してXLAハンドルを初期化します(「XLAの初期化およびXLAハンドルの取得」を参照)。
XLAハンドルをデータベースに対して初期化した後、次の項の手順を実行します。
ここで説明するXLA関数については、第9章「XLAリファレンス」を参照してください。
データベース間で更新レコードを送信する場合は、マスター・データベースとサブスクライバ・データベースの表に互換性があることを確認します。
ttXlaTableByName
、ttXlaGetTableInfo
およびttXlaGetColumnInfo
関数を使用して、表とその列の記述を確認できます。次の項「表および列の記述の確認」を参照してください。
ttXlaVersionTableInfo
およびttXlaVersionColumnInfo
関数を使用して、特定のXLAレコードの表および列のバージョンを確認できます。次の「表および列のバージョンの確認」を参照してください。
ttXlaTableByName
、ttXlaGetTableInfo
およびttXlaGetColumnInfo
関数を使用すると、レプリケートする各表のttXlaTblDesc_t
およびttXlaColDesc_t
記述が返されます。これらの処理については、「更新を監視する表の指定」および「列の記述の取得」を参照してください。次に、これらの記述をttXlaTableCheck
関数に渡すことができます。出力パラメータcompat
は、表に互換性があるかどうかを指定します。値が1の場合は互換性があり、値が0(ゼロ)の場合は互換性がないことを示します。次の例でこれを説明します。
ttXlaVersionTableInfo
およびttXlaVersionColumnInfo
関数を使用して、レコードの生成時に更新レコードの表構造の情報を取得します。
次の例では、pCmd
ソースのpXlaRecord
更新レコードに関係付けられている表がhXlaTarget
ターゲットと互換性があることを確認します。
例5-19 表および列のバージョンの互換性の確認
BOOL CUTLCheckXlaTable (SCOMMAND* pCmd, ttXlaHandle_h hXlaTarget, const ttXlaUpdateDesc_t* pXlaRecord) { /* locals */ ttXlaTblVerDesc_t tblVerDescSource; ttXlaColDesc_t colDescSource [255]; SQLINTEGER iColsReturned = 0; SQLINTEGER iCompatible = 0; SQLRETURN rc; /* only certain update record types should be checked */ if (pXlaRecord->type == INSERTTUP || pXlaRecord->type == UPDATETUP || pXlaRecord->type == DELETETUP) { /* Get source table description associated with this record */ /* from the time it was generated. */ rc = ttXlaVersionTableInfo (pCmd->pCtx->con->hXla, (ttXlaUpdateDesc_t*) pXlaRecord, &tblVerDescSource); if (rc == SQL_SUCCESS) { /* Get the source column descriptors for this table */ /* at the time the record was generated. */ rc = ttXlaVersionColumnInfo (pCmd->pCtx->con->hXla, (ttXlaUpdateDesc_t*) pXlaRecord, colDescSource, 255, &iColsReturned); if (rc == SQL_SUCCESS) { /* Check compatibility. */ rc = ttXlaTableCheck (hXlaTarget, &tblVerDescSource.tblDesc, colDescSource, &iCompatible); } } } }
レプリケートを開始する準備の完了後、ttXlaNextUpdate
またはttXlaNextUpdateWait
関数を使用してマスター・データベースから更新レコードのバッチを取得し、ttXlaApply
を使用してサブスクライバ・データベースにレコードを書き込みます。この例を次に示します。
例5-20 データベース間での更新のレプリケート
int j; ttXlaHandle_h h; SQLINTEGER records; ttXlaUpdateDesc_t** arry; do { /* get up to 15 updates */ rc = ttXlaNextUpdate(h,&arry,15,&records); if (rc != SQL_SUCCESS) { /* See "Handling XLA errors" */ } /* print number of updates returned */ printf("Records returned by ttXlaNextUpdate : %d\n",records); /* apply the received updates */ for (j=0;j < records;j++) { ttXlaUpdateDesc_t* p; p = arry[j]; rc = ttXlaApply(h, p, 0); if (rc != SQL_SUCCESS){ /* See "Handling XLA errors" and */ /* "Handling timeout and deadlock errors" below */ } } /* print number of updates applied */ printf("Records applied successfully : %d\n",records); } while (records != 0);
重要: ネットワーク上、または同じメモリー領域を使用しないプロセス間の任意の場所でレプリケートするデータをパッケージ化する場合は、ttXlaUpdateDesc_t データ構造体を完全に含める必要があります。その長さはttXlaUpdateDesc_t -> header.length で示され、header 要素はttXlaNodeHdr_t 構造体で、この構造体はlength 要素を持ちます。「ttXlaUpdateDesc_t」および「ttXlaNodeHdr_t」も参照してください。 |
ttXlaApply
からのリターン・コードは、更新が正常に行われたかどうかを示します。リターン・コードがSQL_SUCCESS
でない場合は、更新で一時的な問題(デッドロック、タイムアウトなど)または永続的な問題が発生した可能性があります。ttXlaError
を使用して、tt_ErrDeadlockVictim
、tt_ErrTimeoutVictim
などのエラーを確認できます。一時的なエラーは、レプリケートしたトランザクションをロールバックし、再実行することでリカバリできます。その他のエラーは、重複キー違反、キーが見つからないなどの永続的エラーである可能性があります。このようなエラーは、トランザクションを再実行しても、繰り返し発生する可能性があります。
ttXlaApply
が、トランザクションのコミット・レコード(ttXlaUpdateDesc_t ->
flags
= TT_UPDCOMMIT
)をサブスクライバ・データベースに適用する前にタイムアウトまたはデッドロック・エラーを返した場合は、次のいずれかの処理を実行できます。
ttXlaRollback
を使用してトランザクションをロールバックします。
ttXlaCommit
を使用して、レコードで、サブスクライバ・データベースに適用済の変更をコミットします。
一時的なエラーからのリカバリを有効にするには、マスター・データベースのトランザクション境界を追跡し、サブスクライバに現在適用中のトランザクションに関連付けられているレコードをユーザー・バッファに保存することで、必要に応じてそれらのレコードを再適用できます。トランザクション境界は、ttXlaUpdateDesc_t
構造体のflags
メンバーを確認することで検出できます。次の例を想定してください。この条件がtrueの場合、レコードはコミットされています。
(pXlaRecords [iRecordIndex]->flags & TT_UPDCOMMIT)
トランザクションをロールバックする必要があるエラーが発生した場合は、ttXlaRollback
をコールして、サブスクライバ・データベースに適用済のレコードをロールバックします。その後、ttXlaApply
をコールして、バッファに保存されているすべてのロールバック・レコードを再適用します。
注意: トランザクション・レコードをユーザー・バッファに保存するかわりに、ttXlaGetLSN をコールしてトランザクション・ログ内の各コミット・レコードのトランザクション・ログ・レコード識別子を取得する方法があります(「ブックマークの位置の変更」を参照)。トランザクションをロールバックする必要があるエラーが発生した場合は、ttXlaSetLSN をコールして、ブックマークをトランザクション・ログ内のトランザクションの先頭に再設定し、レコードを再適用します。ただし、この方法は、ttXlaGetLSN 関数関連の追加のオーバーヘッドが発生するため、効率が悪くなる場合があります。 |
アプリケーションで、マスター・データベースおよびサブスクライバ・データベースの両方に対して同時に更新を行うと、更新競合が発生する場合があります。更新競合の詳細は、『Oracle TimesTen In-Memory Database開発者および管理者ガイド』のレプリケーション競合の解消に関する説明を参照してください。
XLAで更新競合を確認するには、ttXlaApply
のtest
パラメータを設定して、UPDATETUP
型の各レコードのold row値(ttXlaUpdateDesc_t ->
tuple1)を、サブスクライバ・データベースの既存の行と比較します。更新記述のold row値がサブスクライバ・データベースの対応する行と一致しない場合は、原因はおそらく更新競合です。この場合、
ttXlaApply
はサブスクライバへの更新を適用せず、sb_ErrXlaTupleMismatch
エラーを返します。
TimesTen以外のデータベースへの変更をレプリケートする場合は、ttXlaGenerateSQL
関数を使用して、レコード・データをTimesTen以外のサブスクライバで読取りが可能なSQL文に変換します。更新および削除のレコードの場合、正しいSQLを生成するために、ttXlaGenerateSQL
には、NULL値可能でない列に対する主キー索引または一意索引が必要です。
ttXlaGenerateSQL
関数は、パラメータとしてttXlaUpdateDesc_t
レコードを受け入れ、それと同等のSQLをバッファに出力します。
重要: ttXlaGenerateSQL によって返されるSQLでは、TimesTen SQL構文を使用します。2つのシステム間でSQL構文に互換性がない場合、TimesTen以外のサブスクライバではSQL文が失敗することがあります。さらに、SQL文は、XLAハンドルに関連付けられている接続キャラクタ・セットでエンコードされます。 |
例5-21 TimesTen以外のデータベースへの更新のレプリケート
この例では、レコード(record
)を変換し、その結果のSQL出力を200文字のバッファ(buffer
)に保存します。バッファの実際のサイズはactualLength
パラメータによって返されます。
ttXlaUpdateDesc_t record; char buffer[200]; SQLINTEGER actualLength; rc = ttXlaGenerateSQL(xla_handle, &record, buffer, 200, &actualLength); if (rc != SQL_SUCCESS) { handleXLAerror (rc, xla_handle, err_buf, &native_error); if ( native_error == 8034 ) { // tt_ErrXlaNoSQL printf("Unable to translate to SQL\n"); } }
その他のXLA機能の使用方法について説明します。内容は次のとおりです。
接続中は、随時ttXlaGetLSN
関数をコールして現行読取りログ・レコード識別子に関してシステムに問い合せることができます。一連の更新を繰り返し実行する必要がある場合は、ttXlaSetLSN
関数を使用して、現行読取りログ・レコード識別子を最後のttXlaAcknowledge
コールによって設定された初期読取りログ・レコード識別子より大きい有効な値に再設定できます。ここで、「大きい」とは、比較対象のログ・レコード識別子が同じトランザクション内のレコードのものである場合にのみ意味を持ちます。そうでない場合、ログ・レコード識別子の数値が大きいとしても、別のトランザクションより前にコミットされたトランザクションのログ・レコード識別子は「より小さい」ログ・レコード識別子になります。初期読取りログ・レコード識別子を現行読取りログ・レコード識別子まで進める方法は、ttXlaAcknowledge
関数をコールする方法のみで、この関数によって、現行読取りログ・レコード識別子までのすべてのトランザクション・ログ・レコードを取得および処理したということが示されます。特定のブックマークに対してttXlaAcknowledge
をコールすると、現行読取りログ・レコード識別子より小さいログ・レコード識別子を持つトランザクション・ログ・レコードにアクセスできなくなります。
XLA関数ではありませんが、トランザクション・ログに対するライターでttApplicationContext
組込みプロシージャをコールして、アプリケーションに関連付けられているバイナリ・データをXLAリーダーに渡すことができます。このプロシージャは、現在のトランザクションが生成する次の更新レコードに返す1つのVARBINARY
値を指定します。XLAリーダーでは、「NOT INLINE可変長列データの読取り」で説明されている方法で、この値へのポインタを取得できます。
注意: コンテキスト値は、1つの更新レコードにのみ適用されます。適用後に値が再設定されます。同じコンテキスト値を複数の更新に適用する場合は、各更新の前に再設定する必要があります。 |
コンテキストを設定するには、次の手順を実行します。
ttApplicationContext
プロシージャを起動するために2つのプログラム変数を宣言します。変数contextBuffer
は、最も長いアプリケーション・コンテキストを格納できる十分な大きさを持つように宣言されたCHAR
配列です。contextBufferLen
は、INTEGER
型の変数で、ttApplicationContext
への各コールでコンテキストの実際の長さを伝えるために使用されます。
ttApplicationContext
組込みプロシージャのコンパイル済の起動によって文ハンドルを初期化します。
rc = SQLPrepare(hstmt, "call ttApplicationContext(?)", SQL_NTS); rc = SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_VARBINARY, 0, 0, &contextBuffer, sizeof contextBuffer, &contextBufferLen);
アプリケーション・コンテキストを後で設定する必要がある場合は、コンテキスト値をcontextBuffer
にコピーし、コンテキストの長さをcontextBufferLen
に割り当て、次のコールを使用してttApplicationContext
を起動します。
rc = SQLExecute(hstmt);
トランザクションは、SQLTransact
への通常のコールでコミットされます。
rc = SQLTransact(NULL, hdbc, SQL_COMMIT);
注意: ttApplicationContext をコールした後にSQL処理に失敗すると、コンテキストが次のSQL処理に渡されず、失われる可能性があります。このような問題が発生した場合は、アプリケーションで、次のSQL処理の前にttApplicationContext を再度コールできます。 |