ヘッダーをスキップ
Oracle TimesTen In-Memory Database C開発者ガイド
リリース11.2.1
B56048-01
  目次
目次
索引
索引

戻る
戻る
 
次へ
次へ
 

5 XLAおよびTimesTenイベント管理

トランザクション・ログAPI(XLA)はC言語関数のセットです。この関数によって、次の処理を実行するアプリケーションを実装できます。

XLAの目的の1つは、トリガーにかわる高パフォーマンスで非同期の機能を提供することです。

また、『Oracle TimesTen In-Memory Database TimesTen to TimesTen開発者および管理者ガイド』で説明するTimesTenレプリケーション・ソリューションでは要件が満たされない場合、カスタム・データ・レプリケーション・ソリューションを構築するための関数がXLAによって提供されます。

各XLA関数の詳細は、第9章「XLAリファレンス」を参照してください。


注意:

  • XLAは、TimesTenでサポートされているすべてのプラットフォームで使用できます。 ただし、異なるプラットフォーム間、または同じプラットフォームの32ビットと64ビットとの間でのデータの転送はサポートされていません。

  • XLAでは、ドライバ・マネージャ・ライブラリまたはクライアント/サーバー・ライブラリにリンクされたアプリケーションはサポートされていません。


この章の内容は次のとおりです。

XLAの概念

この項の内容は次のとおりです。

ここで説明するXLA関数については、第9章「XLAリファレンス」を参照してください。

XLA永続モード

通常、TimesTen XLAは永続モードで初期化されます。 このモードでは、XLAによって更新レコードがトランザクション・ログ・バッファまたはトランザクション・ログ・ファイルから直接取得されるため、それらの更新レコードは、必要とされるかぎり使用可能です。また、永続的なロギング・モデルによって、複数のリーダーでトランザクション・ログの更新を同時に読み取ることができます。

ttXlaPersistOpen XLA関数は、永続モードでTimesTenデータ・ストアへの接続をオープンします。

TimesTenは、初期作成時に、アプリケーションがリンクされているTimesTenリリースと同じバージョンに、トランザクション・ログ・ハンドルを構成します。 また、ttXlaGetVersionおよびttXlaSetVersion XLA関数を使用して、以前のXLAバージョンと相互運用することもできます。

(XLAを非永続モード使用できますが、お薦めしません。 詳細は、「非永続モードでのXLAの使用」を参照してください。)

XLAでレコードをトランザクション・ログから読み取る方法

アプリケーションによってTimesTenデータ・ストアが変更されると、TimesTenは、データ・ストアの変更およびトランザクション・コミットなどの他のイベントを示すトランザクション・ログ・レコードを生成します。

新しいトランザクション・ログ・レコードは、生成されると常にログ・バッファの最後に書き込まれます。

トランザクション・ログ・レコードは、メモリー内のログ・バッファからディスク上のトランザクション・ログ・ファイルに定期的にバッチでフラッシュされます。 XLAが永続モードで初期化されている場合、XLAアプリケーションでは、トランザクション・ログのどの部分がディスクまたはメモリーに存在するかについて考慮する必要はありません。 したがって、この章で使用するトランザクション・ログという用語は、トランザクション更新レコードの仮想ソースを指します。これらのレコードが物理的にメモリーまたはディスクに保存されているかどうかは関係ありません。

アプリケーションで、XLAを使用し、TimesTenデータ・ストアへの変更に関してトランザクション・ログを監視できます。 XLAは、トランザクション・ログ全体を読み取り、ログ・レコードをフィルタ処理し、目的の表および列への変更が含まれているトランザクション・レコードのリストをXLAアプリケーションに配信します。

XLAは、レコードをディスクリート・トランザクションにソートします。 複数のアプリケーションが同時にデータ・ストアを更新している場合、異なるアプリケーションのトランザクション・ログ・レコードがトランザクション・ログに交互配置されます。

XLAは、特定のトランザクションに関連するすべてのトランザクション・ログ・レコードを透過的に抽出し、それらを連続するリストにしてアプリケーションに配信します。

コミット済のトランザクションのレコードのみが返されます。コミット済のレコードは、最後のコミット・レコードがトランザクション・ログに書き込まれる順序で返されます。 コミットされていないデータ・ストアへの変更に関連するレコードは、XLAによってフィルタ処理されます。

変更が行われ、その後にロールバックされた場合、強制終了されたトランザクションのレコードはアプリケーションに配信されません。

これらのXLAの基本概念のほぼすべてを例5-1に示し、次にそれぞれの内容を箇条書きで示します。

図5-1に示すトランザクション・ログを例として考えてみます。

図5-1 トランザクション・ログから抽出されるレコード

図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メカニズムの間に処理上の違いはありません。 ただし、非同期のマテリアライズド・ビューの場合、非同期ビューのXLA通知の順序は、関連付けられているディテール表での順序または同期ビューでの順序と同じとはかぎらないことに注意してください。 たとえば、ディテール表に2つの挿入が行われる場合、非同期のマテリアライズド・ビューでは逆の順序で挿入が行われることがあります。 さらに、更新が、削除とその後に続く挿入として扱われることがあります。 また、複数の挿入や複数の削除などの複数の操作が組み合せられる可能性もあります。 したがって、順序に依存するアプリケーションでは、非同期のマテリアライズド・ビューを使用しないでください。

マテリアライズド・ビューの詳細は、次を参照してください。

  • 『Oracle TimesTen In-Memory Database SQLリファレンス・ガイド』のCREATE MATERIALIZED VIEWに関する項

  • 『Oracle TimesTen In-Memory Databaseオペレーション・ガイド』のマテリアライズド・ビューの理解に関する項

XLAブックマークについて

永続トランザクション・ログの各リーダーは、ブックマークを使用して更新ログ・ストリーム内での位置を維持します。 各ブックマークは、ログ・レコード識別子を使用してトランザクション・ログ内の更新レコードを追跡する2つのポインタで構成されています。

  • 初期読取りログ・レコード識別子は、最後に確認されたトランザクション・ログ・レコードを指します。 初期読取りログ・レコード識別子はデータ・ストアに保存されるため、データ・ストアの接続、停止および障害が発生しても永続的に維持されます。

  • 現行読取りログ・レコード識別子は、トランザクション・ログから現在読み取られているレコードを指します。

この項の以降の内容は次のとおりです。

ブックマークの作成または再利用

ttXlaPersistOpen関数をコールして永続XLAハンドルを初期化する場合は、tagパラメータを使用して新しいブックマークまたはシステムに既存のブックマークのいずれかを指定し、optionsパラメータを使用してそのブックマークが新しいレプリケートされていないブックマーク、新しいレプリケートされたブックマークまたは既存(再利用)のブックマークであるかどうかを指定します(「XLAの初期化およびXLAハンドルの取得」を参照)。 この時点で、ブックマークに関連付けられている初期読取りログ・レコード識別子は、データ・ストアから読み取られ、永続XLAハンドル(ttXlaHandle_h)にキャッシュされます。 この識別子は、トランザクション・ログ内のリーダーの開始位置を示します。

ブックマークの動作

アプリケーションが最初にXLAを初期化し、XLAハンドルを取得した時点では、現行読取りログ・レコード識別子と初期読取りログ・レコード識別子は、図5-2に示すように、両方ともデータ・ストアに最後に書き込まれたレコードを指しています。

図5-2 永続XLAハンドルを初期化した時点でのログ・レコード識別子の位置

図5-2の説明が続きます。
図5-2「永続XLAハンドルを初期化した時点でのログ・レコード識別子の位置」の説明

ttXlaNextUpdateまたはttXlaNextUpdateWait関数を使用すると、一連のコミット済トランザクションのレコードが、コミットされた順序でトランザクション・ログから返されます(「トランザクション・ログからの更新レコードの取得」を参照)。 図5-3に示すように、ttXlaNextUpdateをコールするたびに、ブックマークの現行読取りログ・レコード識別子は、最後に読み取られたレコードに再設定されます。 現行読取りログ・レコード識別子によって、次にttXlaNextUpdateをコールする場合の開始位置がマークされます。

図5-3 ttXlaNextUpdateによって取得されるレコード

図5-3の説明が続きます。
図5-3「ttXlaNextUpdateによって取得されるレコード」の説明

ttXlaGetLSNおよびttXlaSetLSN関数を使用すると、レコードを再度読み取ることができます(「ブックマークの位置の変更」を参照)。 ただし、図5-4に示すように、ttXlaAcknowledge関数をコールすると、ブックマークの初期読取りログ・レコード識別子が現行読取りログ・レコード識別子に永続的に再設定されます。 ttXlaAcknowledge関数をコールして初期読取りログ・レコード識別子を再設定すると、以前に読み取られたすべてのトランザクション・レコードにパージのフラグが設定されます。 初期読取りログ・レコード識別子を再設定すると、ttXlaSetLSN関数を使用して元に戻し、以前に読み取ったトランザクションを再度読み取ることはできません。

図5-4 ttXlaAcknowledgeによるブックマークの再設定

図5-4の説明が続きます。
図5-4「ttXlaAcknowledgeによるブックマークの再設定」の説明


注意:

ttXlaAcknowledge関数をコールすると、確認する関連更新レコードがない場合でもブックマークがリセットされます。 これは、トランザクション・ログ領域の管理に役立ちますが、処理コストとのバランスを保つ必要があります。 XLAでは、トランザクション・ログが一度に1ファイルずつパージされる点に注意してください。 処理の詳細は、「ttXlaAcknowledge」を参照してください。

TimesTenデータ・ストアで作成されるブックマークの数は64に制限されています。各ブックマークに一度に関連付けることができるアクティブな永続接続は1つのみです。ただし、ブックマークは、存続期間中に多数の接続と関連付けることができます。 アプリケーションによって、永続接続をオープンし、新しいブックマークを作成し、そのブックマークを接続と関連付け、ブックマークを使用してレコードを読み取り、接続をデータ・ストアから切断し、データ・ストアに再接続して、新しい永続接続を作成し、この新しい接続を既存のブックマークと関連付け、以前の接続が停止された永続トランザクション・ログ・レコードから読取りを続行することができます。

レプリケートされたブックマーク

アクティブ・スタンバイ・ペアのレプリケーション・スキームを使用する場合は、ttXlaPersistOpenコールのoptionsの設定に従ってレプリケートされたブックマークを使用できます。レプリケートされたブックマークの場合、ブックマークに対する処理は必要に応じてスタンバイ・データベースにレプリケートされます。これにより、フェイルオーバーが発生した場合、より効率的にブックマークの位置をリカバリできます。 読取りは、新しいアクティブ・ストアへのスイッチオーバー前に読取りが中断された位置に近いXLAレコードのストリームから再開されます。 レプリケートされたブックマークがない場合、古いアクティブ・ストアにすでに返されている多数の重複レコードを読み取る必要があります。

アクティブ・データベース内のレプリケートされたブックマークのみを読み取って確認できます。 レプリケートされたブックマークを確認するたびに、確認処理がスタンバイ・データベースに非同期にレプリケートされます。

使用に関しては、次の点に注意してください。

  • スタンバイ・データベースでのブックマークの位置は、アクティブ・データベースでのブックマークの位置に非常に近くなります。ただし、確認処理のレプリケーションは非同期に行われるため、確認処理の実行頻度によっては、フェイルオーバーの発生時に更新の重複を示す小さなウィンドウが表示されることがあります。

  • データ・ストアがスタンバイ・ステータスからアクティブ・ステータスに変わった後、ttXlaClose関数とttXlaPersistOpen関数を使用して、そのデータ・ストア上のすべてのブックマークを閉じ、再度開くすることをお薦めします。 ブックマークは、レプリケーション・エージェントによってスタンバイ・データ・ストア上で必要に応じて自動的に再配置されるため、スタンバイ・データ・ストア上のレプリケートされたブックマークの状態は、通常のXLA処理中に変化します。 したがって、データ・ストアがアクティブ・ステータスに変更される前に開いていたブックマークを使用しようとすると、ブックマークの状態がリセットされたこととブックマークが再配置されたことを示すエラーが表示されます。 この場合でも再配置されたブックマークから読取りを続行できますが、ブックマークを閉じて再度開くと、エラーを回避できます。

  • アクティブ・スタンバイ・ペアのスキームを有効にした時点でレプリケートされたブックマークがすでに存在する場合、そのブックマークは自動的にレプリケーション・スキームに追加されます。

  • レプリケートされたブックマークが存在する間に、アクティブ・スタンバイ・ペアのスキームを削除できます。 削除した時点でブックマークはレプリケートされなくなります。

  • レプリケーション・エージェントの実行中は、レプリケートされたブックマークを削除できません。

XLAデータ型について

相当するデータ型がTimesTenリリース7.0より前に存在していた場合、TimesTenでサポートされるXLAデータ型は以前のデータ型と同じです。したがって、リリース7.0より前に作成されたXLAアプリケーションは、コードを変更しなくても動作するはずです。リリース7.0より前に作成されたXLAアプリケーションで新しいデータ型を使用するように変更する場合は、新しいデータ型をサポートするように変更する必要があります。

表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データ型

TT_CHAR

SQL_CHAR

TTXLA_CHAR_TT

TT_VARCHAR

SQL_VARCHAR

TTXLA_VARCHAR_TT

TT_NCHAR

SQL_WCHAR

TTXLA_NCHAR_TT

TT_NVARCHAR

SQL_WVARCHAR

TTXLA_NVARCHAR_TT

CHAR

-

TTXLA_CHAR

NCHAR

-

TTXLA_NCHAR

VARCHAR2

-

TTXLA_VARCHAR

NVARCHAR2

-

TTXLA_NVARCHAR

TT_TINYINT

SQL_TINYINT

TTXLA_TINYINT

TT_SMALLINT

SQL_SMALLINT

TTXLA_SMALLINT

TT_INTEGER

SQL_INTEGER

TTXLA_INTEGER

TT_BIGINT

SQL_BIGINT

TTXLA_BIGINT

BINARY_FLOAT

SQL_REAL

TTXLA_BINARY_FLOAT

BINARY_DOUBLE

SQL_DOUBLE

TTXLA_BINARY_DOUBLE

TT_DECIMAL

SQL_DECIMAL

TTXLA_DECIMAL_TT

NUMBER

-

TTXLA_NUMBER

NUMBER(p,s)

-

TTXLA_NUMBER

FLOAT

-

TTXLA_NUMBER

TT_TIME

SQL_TIME

TTXLA_TIME

TT_DATE

SQL_DATE

TTXLA_DATE_TT

TT_TIMESTAMP

SQL_TIMESTAMP

TTXLA_TIMESTAMP_TT

DATE

-

TTXLA_DATE

TIMESTAMP

-

TTXLA_TIMESTAMP

TT_BINARY

SQL_BINARY

TTXLA_BINARY

TT_VARBINARY

SQL_VARBINARY

TTXLA_VARBINARY

ROWID

-

TTXLA_ROWID


XLAでは、内部SQLデータ型と外部プログラム・データ型を変換する関数が提供されます。たとえば、ttXlaNumberToCStringを使用すると、NUMBER列をキャラクタ文字列に変換できます。 XLAデータ型変換関数は次のとおりです。

アクセス制御がXLAに与える影響

データベース内の処理に与えるTimesTenアクセス制御の影響の概要については、「アクセス制御に関するTimesTen機能の考慮事項」を参照してください。 アクセス制御がXLAに与える影響は、次のとおりです。

  • XLA機能の実行には、システム権限XLAが必要になります。 次の処理で必要になります。

    • ttXlaPersistOpen C関数を使用するなど、XLAリーダーとしてのTimesTenへの接続。

    • その他のXLA関連のTimesTen C関数の実行。 これらの関数については、第9章「XLAリファレンス」を参照してください。

    • XLA関連のTimesTen組込み関数の実行。 関数ttXlaBookmarkCreatettXlaBookmarkDeletettXlaSubscribeおよびttXlaUnsubscribeについては、『Oracle TimesTen In-Memory Databaseリファレンス』の組込みプロシージャに関する項を参照してください。

  • XLA権限を持つユーザーには、SELECT ANY TABLEおよびSELECT ANY SEQUENCEシステム権限と同等の権限があります。

  • XLA権限を持つユーザーは、データベースで発生したDDL文レコードを取得できます。 その結果、このユーザーは、XLA権限を持たない場合にはアクセスが許可されていないデータベース・オブジェクトに関する情報を取得できる点に注意してください。

XLAのデモ

TimesTenには、この章で説明する多数のXLA関数の使用方法を示すxlaSimpleデモが用意されています。 このデモはquickstart/sample_code/odbc/xlaディレクトリにあります。

C開発者向けのTimesTenデモ・プログラムの概要は、「TimesTen Cのデモについて」を参照してください。 詳細は、install_dir/quickstart.htmlを参照してください。 odbcディレクトリのREADMEファイルには、xlaSimpleのビルドおよび実行の手順が記載されています。

次の項「XLAイベント・ハンドラ・アプリケーションの作成」に示すサンプル・コードなど、この章の大部分の内容は、このxlaSimpleデモに基づいています。 このデモでは、表MYDATAAPPUSERスキーマに作成されています。 APPUSERとしてログインしている間は、その表を更新することになります。 XLAUSERとしてログインしている間は、xlaSimpleデモが更新についてレポートします。

デモを実行するには、コマンド・プロンプトでxlaSimpleを実行します。 サンプル・データベースの作成時に指定したXLAUSERのパスワードを入力するように求められます。 別のコマンド・プロンプトでttIsqlを開始して、APPUSERとしてTimesTenサンプル・データベースに接続します。 再度、サンプル・データベースの作成時に指定したパスワードの入力を求められます。

ttIsqlコマンド・プロンプトで、DML文を入力して表を変更できます。 その後、xlaSimpleウィンドウでXLA出力を表示できます。

XLAイベント・ハンドラ・アプリケーションの作成

この項では、データ・ストア内の選択した表への変更を検出およびレポートするXLAアプリケーションの一般的な作成手順について説明します。 「列データの確認」のような例外もありますが、この項で説明する手順は、ほぼすべてのXLAアプリケーションに適用できます。

この項で説明する手順は次のとおりです。

この項のコード例は、xlaSimpleデモ・アプリケーションに基づいています。

ここで説明するXLA関数については、第9章「XLAリファレンス」を参照してください。


重要:

「TimesTen #includeファイル」に記述されている#includeファイル以外に、XLAアプリケーションにはtt_xla.hを含める必要があります。


注意:

コード例をわかりやすくするために、各関数コールのルーチン・エラー・チェック・コードは省略されています。 エラーの処理方法については、「XLAエラーの処理」を参照してください。

データ・ストア接続ハンドルの取得

すべてのODBCアプリケーションと同様に、XLAアプリケーションは、ODBCを初期化して環境ハンドル(henv)を取得し、接続ハンドル(hdbc)を取得して特定のTimesTenデータ・ストアと通信する必要があります。

環境ハンドルおよび接続ハンドルを初期化します。

SQLHENV henv = SQL_NULL_HENV;
SQLHDBC hdbc = SQL_NULL_HDBC;

henvのアドレスをSQLAllocEnv ODBC関数に渡して環境ハンドルを割り当てます。

rc = SQLAllocEnv(&henv);

hdbcのアドレスをSQLAllocConnect ODBC関数に渡してTimesTenデータ・ストア用の接続ハンドルを割り当てます。

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);

XLAの初期化およびXLAハンドルの取得

「データ・ストア接続ハンドルの取得」の説明に従ってODBCを初期化し、環境ハンドルおよび接続ハンドルを取得した後、XLAを初期化し、XLAハンドルを取得して、トランザクション・ログにアクセスできます。1つのODBC接続に対して1つのみの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ハンドルの初期作成時に、TimesTenはアプリケーションがリンクされているTimesTenリリースと同じバージョンに、XLAハンドルを構成します。 以前のXLAバージョンと相互運用する必要がある場合は、ttXlaGetVersionおよびttXlaSetVersion関数を使用できます。

更新を監視する表の指定

「XLAの初期化およびXLAハンドルの取得」の説明に従ってXLAを初期化し、XLAハンドルを取得した後、更新イベントを監視する表またはマテリアライズド・ビューを指定できます。

SYS.XLASUBSCRIPTIONS表を問い合せることによって、ブックマークをサブスクライブする表を確認できます。 また、SYS.XLASUBSCRIPTIONSを使用すると、特定の表にサブスクライブしたブックマークを確認することもできます。

ttXlaNextUpdateおよびttXlaNextUpdateWait関数は、DDLイベントに関連するXLAレコードを取得します。 DDL XLAレコードはどのXLAブックマークでも使用できます。 DDLイベントには、CREATAB、DROPTAB、CREAIND、DROPIND、CREATVIEW、DROPVIEW、CREATSEQ、DROPSEQ、ADDCOLS、DRPCOLS、TRUNCATE、SETTBLIおよびSETCOLIトランザクションが含まれています。

ttXlaTableStatus関数は、指定した表に関連するDMLレコードを現在のブックマークで監視する必要があることを指定します。 または、現在のブックマークが表に関連するDMLレコードをすでに監視しているかどうかを判別します。

ttXlaTableByName関数をコールして、指定した表またはマテリアライズド・ビューのシステム識別子およびユーザー識別子の両方を取得します。 次に、ttXlaTableStatus関数をコールして、表またはマテリアライズド・ビューへの変更を監視するためにXLAを有効にします。

例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パラメータは、コール時の表のステータスを示す出力です。

ttXlaTableStatusnewstatusを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関数をコールして、トランザクション・ログから一連のレコードを返すことができます。 コミット済のトランザクションのレコードのみが返されます。 レコードはコミットされた順序で返されます。 ttXlaAcknowledge関数を定期的にコールして、トランザクションの受信を確認する必要があります。これによって、不要になり、トランザクション・ログからパージ可能なレコードを判別できます。 これらの関数は、トランザクション・ログ内のアプリケーションのブックマークの位置に影響を与えます(「ブックマークの動作」を参照)。


注意:

ttXlaAcknowledgeは、必要な場合にのみ使用する必要がある高コストの処理です。

ttXlaNextUpdateによって返されたトランザクション内の各更新レコードは、ttXlaUpdateDesc_t構造体で記述される更新ヘッダーで始まります。 この更新ヘッダーには、レコードがトランザクションの最初のレコードか(TT_UPDFIRST)、最後のコミット・レコード(TT_UPDCOMMIT)かを示すフラグが格納されています。また、更新ヘッダーは、更新によって影響を受ける表も識別します。更新ヘッダーの後には、データ・ストア内の表に対して行われた変更について記述する0(ゼロ)から2行の行データが続きます。

図5-5に、4つの更新レコードで構成されているトランザクションをトランザクション・ログから返すttXlaNextUpdateへのコールを示します。返されたトランザクションの受信は、ttXlaAcknowledgeをコールすることで確認され、ブックマークが再設定されます。


注意:

この例は、説明を明確にするために簡略化されています。 実際のXLAアプリケーションでは、ttXlaAcknowledgeをコールする前に、複数のトランザクションのレコードを読み取る可能性があります。

図5-5 更新レコード

図5-5の説明が続きます。
図5-5「更新レコード」の説明

例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つのバッチで返されます。 1つ目のバッチには、CTの最初の10個のレコード(nreturned = 10)が含まれます。 2つ目のバッチには、CTの最後のレコードおよびETトランザクションの2つのレコード(ETの後のトランザクションのコミット・レコードが次の7つのレコード内で検出されないと想定)が含まれます。

これらの関数のパラメータの詳細は、「ttXlaNextUpdate」および「ttXlaNextUpdateWait」を参照してください。

図5-6 maxrecords=10の場合に取得されるレコード

図5-6の説明が続きます。
図5-6「maxrecords=10の場合に取得されるレコード」の説明

XLAは、レコードをメモリー・バッファまたはディスク上のトランザクション・ログ・ファイルから読み取ります(「XLAでレコードをトランザクション・ログから読み取る方法」を参照)。待機時間を最小にするために、メモリー・バッファからのレコードは使用可能になるとすぐに返されますが、バッファにないレコードはバッファが空の場合にのみ返されます。この設計によって、XLAアプリケーションでは、行われた変更を最小の待機時間ですぐに表示できます。 トレードオフとして、ttXlaNextUpdateまたはttXlaNextUpdateWaitmaxrecordsパラメータでリクエストした数より少ない数の変更が返される場合があります。


注意:

一部のXLAアプリケーションでは、フェッチおよびレコード処理の手順を非同期にすることでパフォーマンスを向上できます。 たとえば、1つのスレッドを作成してレコードのフェッチおよび保存を行ったり、1つ以上のスレッドを作成して保存したレコードを処理することができます。

レコード・ヘッダーの確認および行アドレスの検出

更新レコードの配列が存在し、その各レコードが示す処理のタイプがわかるため、返された行データを確認できます。

ttXlaNextUpdateまたはttXlaNextUpdateWait関数によって返される各レコードは、ttXlaUpdateDesc_tヘッダーで始まります。このヘッダーには次の情報が記述されています。

  • 処理が実行された表

  • レコードがトランザクションの最初のレコードであるか、または最後の(コミット)レコードであるか

  • レコードが示す処理のタイプ

  • 返された行データの長さ(行データがある場合)

  • 行内の更新された列(該当する列がある場合)

図5-7に、トランザクション・ログ内の更新レコードの例を示します。

図5-7 XLA更新レコードに返される行データのアドレス

図5-7の説明が続きます。
図5-7「XLA更新レコードに返される行データのアドレス」の説明

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に示すように、各行のデータの最初の部分は固定長で、その後に可変長データが続きます。

図5-8 XLA更新レコードに返される行内の列オフセット

図5-8の説明が続きます。
図5-8「XLA更新レコードに返される行内の列オフセット」の説明

列データを確認する手順は次のとおりです。

列記述の取得

返された行から列値を読み取るには、まず、その行の各列のオフセットを判別する必要があります。 列オフセットおよびその他の列メタデータは、特定の表に対して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構造体

図5-9の説明が続きます。
図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-10 行内の固定長データの検出

図5-10の説明が続きます。
図5-10「行内の固定長データの検出」の説明

例5-6 固定長列データの読取り

ここに示すような計算の完全な動作例については、例5-13「完全なPrintColValues()関数」を参照してください。

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値に変換できます(「複合データ型の変換」を参照)。


注意:

XLAによって返される文字列は空文字では終了しません。 「返された文字列の空文字での終了」を参照してください。

NOT INLINE可変長列データの読取り

NOT INLINE可変長データ(VARCHAR、NVARCHARおよびVARBINARY)の場合、ttXlaColDesc_t ->offsetにあるデータは、返された行の可変長部分でのデータの場所を指す4バイトのオフセット値です。オフセット・アドレスをオフセット値に追加すると、行の可変長部分の列データのアドレスを取得できます。この場所の最初のnバイト(nは、32ビット・プラットフォームでは4、64ビット・プラットフォームでは8)はデータの長さです。この後に実際のデータが続きます。 可変長データの場合、ttXlaColDesc_t ->sizeの値は、列の許容最大サイズです。 図5-11に、行内のNOT INLINE可変長データを検出する方法を示します。

図5-11 行内のNOT INLINE可変長データの検出

図5-11の説明が続きます。
図5-11「行内のNOT INLINE可変長データの検出」の説明

例5-7 NOT INLINE可変長列データの読取り

ここに示すような計算の完全な動作例については、例5-13「完全なPrintColValues()関数」を参照してください。

MYDATA表の例で、返された行(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配置に対して繰り返し実行できます。

返された文字列の空文字での終了

レコードの行データから返される文字列は空文字では終了していません。 文字列をバッファにコピーし、その文字列の最後の文字の後に\0などの空文字を追加すると、文字列を空文字で終了できます。

文字列を空文字で終了する手順は、文字列が固定長の場合と可変長の場合で少し異なります。 例5-8に、固定長文字列を空文字で終了する手順を示します。 例5-9には、サイズがわかっている可変長文字列を空文字で終了する手順を示します。 例5-10には、サイズがわかっていない文字列の場合の手順を示します。

例5-8 固定長文字列の空文字での終了

ここに示すような計算の完全な動作例については、例5-13「完全なPrintColValues()関数」を参照してください。

例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「完全なPrintColValues()関数」を参照してください。)

例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データ型変換関数

関数 変換

ttXlaDateToODBCCType

内部TT_DATE値をODBC C値に

ttXlaTimeToODBCCType

内部TT_TIME値をODBC C値に

ttXlaTimeStampToODBCCType

内部TT_TIMESTAMP値をODBC C値に

ttXlaDecimalToCString

内部TT_DECIMAL値を文字列値に

ttXlaDateToODBCCType

内部TTXLA_DATE_TT値をODBC C値に

ttXlaDecimalToCString

内部TTXLA_DECIMAL_TT値をキャラクタ文字列に

ttXlaNumberToBigInt

内部TTXLA_NUMBER値をTT_BIGINT値に

ttXlaNumberToCString

内部TTXLA_NUMBER値をキャラクタ文字列に

ttXlaNumberToDouble

内部TTXLA_NUMBER値を長い浮動小数点数値に

ttXlaNumberToInt

内部TTXLA_NUMBER値を整数に

ttXlaNumberToSmallInt

内部TTXLA_NUMBER値をTT_SMALLINT値に

ttXlaNumberToTinyInt

内部TTXLA_NUMBER値をTT_TINYINT値に

ttXlaNumberToUInt

内部TTXLA_NUMBER値を符号なし整数に

ttXlaOraDateToODBCTimeStamp

内部TTXLA_DATE値をODBCタイムスタンプに

ttXlaOraTimeStampToODBCTimeStamp

内部TTXLA_TIMESTAMP値をODBCタイムスタンプに

ttXlaTimeToODBCCType

内部TTXLA_TIME値をODBC C値に

ttXlaTimeStampToODBCCType

内部TTXLA_TIMESTAMP_TT値をODBC C値に


これらの変換関数は、ttXlaUpdateDesc_t型(UPDATETUP、INSERTTUPおよびDELETETUP)に含まれる行データで使用できます。

例5-11 複合データ型の変換

(ここに示すような計算の完全な動作例については、例5-13「完全なPrintColValues()関数」を参照してください。)

前に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, &timestamp);
  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値の検出

NULL値が含まれる可能性がある列の場合、ttXlaColDesc_t ->nullOffsetはレコードのNULLバイトを指します。 nullOffsetは、列がNULLである場合は1、NULLでない場合は0(ゼロ)となります。

列値がNULLであるかどうかを判断するには、まずnullOffsetが0(ゼロ)かどうかを確認します。0の場合は、NULL値可能ではありません。 nullOffsetがNULL値可能である場合は、nullOffsetの値が1か0かを確認します。

例5-12 NULL値の検出

Column6がNULLかどうかを次のように確認します。

if (xla_column_defs[5].nullOffset != 0) {
  if (*((unsigned char*) tup +
     xla_column_defs[5].nullOffset) == 1) {
         printf("Column6 is NULL\n");
  }
}

すべての列確認コードの組合せ: PrintColValues()関数

例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 ; /* make these global?? */
  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, &timestamp);
        convFunc="ttXlaTimeStampToODBCCType";
      }
      else {
        rc = ttXlaOraTimeStampToODBCTimeStamp(pColVal, &timestamp);
        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 */
}

XLAエラーの処理

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であるかどうかを確認します。 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エラーとコード

エラー コード

tt_ErrDbAllocFailed

802(一時的)

tt_ErrCacheXlaNotRead

5023

tt_ErrCacheXLARollbackFailed

5031

tt_ErrCondLockConflict

6001(一時的)

tt_ErrDeadlockVictim

6002(一時的)

tt_ErrTimeoutVictim

6003(一時的)

tt_ErrPermSpaceExhausted

6220(一時的)

tt_ErrTempSpaceExhausted

6221(一時的)

tt_ErrBadXlaRecord

8024

tt_ErrXlaBookmarkUsed

8029

tt_ErrXlaBookmarkBad

8030

tt_ErrXlaLsnBad

8031

tt_ErrXlaNoSQL

8034

tt_ErrXlaNoLogging

8035

tt_ErrXlaParameter

8036

tt_ErrXlaTableDiff

8037

tt_ErrXlaTableSystem

8038

tt_ErrXlaTupleMismatch

8046

tt_ErrXlaDedicatedConnection

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アプリケーションが接続されており、削除する表を追跡しているブックマークを使用している場合は、次のタスクを実行します。

  1. 各永続XLAアプリケーションでttXlaTableStatus関数をコールし、newstatusパラメータを0に設定します。これにより、アプリケーションで使用中のXLAブックマークから表がサブスクライブ解除されます。

  2. 表を削除します。

永続XLAアプリケーションが接続されておらず、削除する表に関連付けられているブックマークを使用している場合は、次のタスクを実行します。

  1. SYS.XLASUBSCRIPTIONSシステム表を問い合せて、削除する表にサブスクライブされているブックマークを確認します。

  2. ttXlaUnsubscribe組込みプロシージャを使用して、表にサブスクライブされている各XLAブックマークから表をサブスクライブ解除します。

  3. 表を削除します。

ブックマークを削除すると、XLAブックマークからも表がサブスクライブ解除されます。 次の項「ブックマークの削除」を参照してください。

ブックマークの削除

アプリケーションを終了する場合や表を削除する場合に、ブックマークを削除することがあります。 アプリケーションが接続されており、ブックマークを使用している場合は、ttXlaDeleteBookmark関数を使用してXLAブックマークを削除します。

ブックマークは、前の接続がクローズされた後に新しい接続で再利用できます(「XLAブックマークについて」を参照)。新しい接続は、前の接続が停止した場所からトランザクション・ログの読取りを再開します。 次の点に注意してください。

  • ブックマークを削除すると、後続のチェックポイント処理(組込みプロシージャttCkptttCkptBlockingなど)によって、トランザクション・ログ内の未読の更新レコードに関連するディスク領域が解放されます。

  • ブックマークを削除しない場合、XLAアプリケーションは、接続してブックマークを再利用する際に、プログラムの終了以降累積していたすべての未読の更新レコードを読み取ります。これは、更新レコードがTimesTenトランザクション・ログで永続的であるためです。 ただし、これらの未読のレコードがトランザクション・ログ・ファイルに累積し、大量のディスク領域を使用すると危険です。


注意:

  • レプリケーション・エージェントの実行中は、レプリケートされたブックマークを削除できません。

  • ブックマークを再利用する場合は、トランザクション・ログ・ファイル内の初期読取りログ・レコード識別子から開始します。 ブックマークを再利用する接続で、現在の接続を切断した場所から読取りが開始されるようにするには、現在の接続を切断する前にttXlaAcknowledgeをコールして、ブックマークの位置を現在アクセスしているレコードに再設定する必要があります。

  • ttCkptttCkptBlockingにはADMIN権限が必要です。 TimesTenの組込みプロシージャと必要な権限については、『Oracle TimesTen In-Memory Databaseリファレンス』の組込みプロシージャに関する項を参照してください。


例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ブックマークを使用している場合、次のいずれかの方法でブックマークを削除できます。

  • ブックマークをクローズしてttXlaBookmarkDelete組込みプロシージャをコールします。

  • ブックマークをクローズしてttIsqlxladeletebookmarkコマンドを使用します。

XLAアプリケーションの終了

XLAアプリケーションでトランザクション・ログの読取りを終了したら、コミットされていないトランザクションをロールバックし、すべてのハンドルを解放して、正常に終了する必要があります。 また、アプリケーションで未接続時に行われた更新を取得する必要がある場合以外は、監視中の表またはマテリアライズド・ビューをサブスクライブ解除します。 プログラム終了時に、XLAブックマークを削除するかどうかは場合によります(「ブックマークの削除」を参照)。

リソースは、割当ての順序とは逆の順序で解放します。XLAによって追跡された表およびマテリアライズド・ビューの場合は、ttXlaTableStatus関数をコールし、newstatusパラメータを0(ゼロ)に設定します。これによって、表およびマテリアライズド・ビューがXLAからサブスクライブ解除されます。 次に、ttXlaCloseをコールしてXLAハンドルを解放します。

適切なODBC関数をコールします。 SQL_ROLLBACKを指定してSQLTransactをコールし、コミットされていないトランザクションをすべてロールバックします。次に、SQLDisconnectをコールしてTimesTenへの接続をクローズします。 最後に、SQLFreeConnectおよびSQLFreeEnvをコールして、接続ハンドル(hdbc)および環境ハンドル(henv)を解放し、関連するメモリーを解放します。

例5-17 XLAアプリケーションの終了

この例では、xlaSimpleデモのXLA終了関数である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);
}

レプリケーション・メカニズムとしてのXLAの使用

『Oracle TimesTen In-Memory Database TimesTen to TimesTen開発者および管理者ガイド』で説明されているTimesTenレプリケーション・ソリューションが要件を満たさない場合は、XLA関数を使用してデータ・ストア間で更新をレプリケートできます。


注意:

異なるプラットフォーム間または同じプラットフォームの32ビットと64ビットのバージョン間では、XLAを使用して更新をレプリケートできません。

この項では、送信側のデータ・ストアをマスター、受信側のデータ・ストアをサブスクライバと呼びます。 XLAを使用してデータ・ストア間で変更をレプリケートするには、まずttXlaPersistOpen関数を使用してXLAハンドルを初期化します(「XLAの初期化およびXLAハンドルの取得」を参照)。

XLAハンドルをデータ・ストアに対して初期化した後、次の項の手順を実行します。

ここで説明するXLA関数については、第9章「XLAリファレンス」を参照してください。

データ・ストア間での表の互換性の確認

データ・ストア間で更新レコードを送信する場合は、マスター・データ・ストアとサブスクライバ・データ・ストアの表に互換性があることを確認します。

表および列の記述の確認

ttXlaTableByNamettXlaGetTableInfoおよびttXlaGetColumnInfo関数を使用すると、レプリケートする各表のttXlaTblDesc_tおよびttXlaColDesc_t記述が返されます。 これらの処理については、「更新を監視する表の指定」および「列の記述の取得」を参照してください。 これらの記述は、ttXlaTableCheck関数に渡すことができます。出力パラメータcompatは、表に互換性があるかどうかを示します。値が1の場合は互換性があり、値が0(ゼロ)の場合は互換性がないことを示します。次に例を示します。

例5-18 表および列の記述の互換性の確認

SQLINTEGER compat;
ttXlaTblDesc_t table;
ttXlaColDesc_t columns[20];

rc = ttXlaTableCheck(xla_handle, &table, columns, &compat);
if (compat) {
    /* Go ahead and start replicating */
}
else {
    /* Not compatible or some other error occurred */
}

表および列のバージョンの確認

ttXlaVersionTableInfoおよびttXlaVersionColumnInfo関数を使用して、レコードの生成時に更新レコードの表構造の情報を取得します。

次の例では、pCmdソースのpXlaRecord更新レコードに関係付けられている表がhXlaTargetターゲットと互換性があることを確認します。

例5-19 表および列のバージョンの互換性の確認

BOOL CUTLCheckXlaTable (SCOMMAND* pCmd,
                        ttXlaHandle_h hXlaTarget,
                        const ttXlaUpdateDesc_t* pXlaRecord)
{
  /* locals */
  BOOL bStatus = TRUE;
  SQLRETURN rc;
  ttXlaTblVerDesc_t tblVerDescSource;
  ttXlaColDesc_t colDescSource [255];
  SQLINTEGER iColumnCount = 0, iCompatible = 0;

  /* 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 */
    /* at the time it was generated */
    rc = ttXlaVersionTableInfo (pCmd->pCtx->con->hXla,
       (ttXlaUpdateDesc_t*) pXlaRecord,
       &tblVerDescSource);

 /* get the source column descriptors for this table */
    /* at the time the record was generated */
    if (bStatus)
    {
      SQLINTEGER iColsReturned = 0;
      rc = ttXlaVersionColumnInfo (pCmd->pCtx->con->hXla,
        (ttXlaUpdateDesc_t*) pXlaRecord,
        colDescSource, 255, &iColsReturned);

その後、この情報をttXlaTableCheck関数に渡して、レコードを別のデータ・ストアに安全に適用できるかどうかを確認します。

/* check compatibility */
    if (bStatus)
    {
      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" */
      }
    }

    /* 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_ErrDeadlockVictimtt_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 TimesTen to TimesTen開発者および管理者ガイド』の競合解消と障害回復に関する項を参照してください。

XLAで更新競合を確認するには、ttXlaApplytestパラメータを設定して、UPDATETUP型の各レコードのold row値(ttXlaUpdateDesc_t ->tuple1)を、サブスクライバ・データ・ストアの既存の行と比較します。 更新記述のold row値がサブスクライバ・データ・ストアの対応する行と一致しない場合は、更新競合が想定されます。 この場合、ttXlaApplyはサブスクライバへの更新を適用せず、sb_ErrXlaTupleMismatchエラーを返します。

TimesTen以外のデータ・ストアへの更新のレプリケート

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機能

その他のXLA機能の使用方法について説明します。内容は次のとおりです。

ブックマークの位置の変更

接続中は、随時ttXlaGetLSN関数をコールして現行読取りログ・レコード識別子に関してシステムに問い合せることができます。 一連の更新を繰り返し実行する必要がある場合は、ttXlaSetLSN関数を使用して、現行読取りログ・レコード識別子を最後のttXlaAcknowledgeコールによって設定された初期読取りログ・レコード識別子より大きい有効な値に再設定できます。 ここで、「大きい」とは、比較対象のログ・レコード識別子が同じトランザクション内のレコードのものである場合にのみ意味を持ちます。 そうでない場合、ログ・レコード識別子の数値が大きいとしても、別のトランザクションより前にコミットされたトランザクションのログ・レコード識別子はより小さいログ・レコード識別子になります。 初期読取りログ・レコード識別子を現行読取りログ・レコード識別子まで進める方法は、ttXlaAcknowledge関数をコールする方法のみです。この関数によって、現行読取りログ・レコード識別子までのすべてのトランザクション・ログ・レコードを取得および処理したということが示されます。 特定のブックマークに対してttXlaAcknowledgeをコールすると、現行読取りログ・レコード識別子より小さいログ・レコード識別子を持つトランザクション・ログ・レコードにアクセスできなくなります。

アプリケーション・コンテキストの受渡し

XLA関数ではありませんが、トランザクション・ログに対するライターでttApplicationContext組込みプロシージャをコールして、アプリケーションに関連付けられているバイナリ・データをXLAリーダーに渡すことができます。 このプロシージャは、現在のトランザクションが生成する次の更新レコードに返す1つのVARBINARY値を指定します。 XLAリーダーでは、「NOT INLINE可変長列データの読取り」で説明されている方法で、この値へのポインタを取得できます。


注意:

コンテキスト値は、1つの更新レコードにのみ適用されます。 適用後に値が再設定されます。 同じコンテキスト値を複数の更新に適用する場合は、各更新の前に再設定する必要があります。

コンテキストを設定するには、次の手順を実行します。

  1. ttApplicationContextプロシージャを起動するために2つのプログラム変数を宣言します。contextBufferは、最も長いアプリケーション・コンテキストを格納できる十分な大きさを持つように宣言されたCHAR配列です。 contextBufferLenは、INTEGER型の変数で、ttApplicationContextへの各コールでコンテキストの実際の長さを伝えるために使用されます。

  2. 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);
    
  3. アプリケーション・コンテキストを後で設定する必要がある場合は、コンテキスト値をcontextBufferにコピーし、コンテキストの長さをcontextBufferLenに割り当て、次のコールを使用してttApplicationContextを起動します。

    rc = SQLExecute(hstmt);
    

    トランザクションは、SQLTransactへの通常のコールでコミットされます。

    rc = SQLTransact(NULL, hdbc, SQL_COMMIT);
    

    注意:

    ttApplicationContextをコールした後にSQL処理に失敗すると、コンテキストが次のSQL処理に渡されず、失われる可能性があります。 このような問題が発生した場合は、アプリケーションで、次のSQL処理の前にttApplicationContextを再度コールできます。

非永続モードでのXLAの使用

TimesTen XLAは、通常は永続モードで使用されますが、非永続モードもサポートされています。 これは、主に下位互換性を保つためです。 非永続モードでは、トランザクション・ログの更新はXLAステージング・バッファに保持されます。XLAは、XLAステージング・バッファでトランザクション・ログから取得した更新レコードをステージングし、アプリケーションで読取りを行うことができるようにします。ただし、ステージング・バッファには一度に1つのリーダーのみがアクセス可能であり、コンピュータまたはデータ・ストアが停止した場合はバッファ内のすべてのデータが失われます。

ttXlaOpenTimesTen XLA関数は、非永続モードでTimesTenデータ・ストアへの接続をオープンします。

非永続モードでのXLAの処理について説明します。内容は次のとおりです。

非永続モードと永続モードの相違点

非永続モードは、次の点で永続モードと異なります。

  • トランザクション更新レコードは、トランザクション・ログ・バッファまたはディスク上のトランザクション・ログ・ファイルから直接取得されるのではなく、一時ステージング・バッファに保持されています。

  • ステージング・バッファがいっぱいになると、バッファを空にするまでトランザクションを完了できません。

  • XLAブックマークは使用できません。

  • ttXlaConfigBufferを使用してステージング・バッファのサイズを設定する必要があります。

  • ttXlaStatus関数をコールして、ステージング・バッファのステータスを確認できます。

  • ttXlaResetStatus関数をコールして、ステージング・バッファのステータスを再設定できます。

  • いずれの時点でも、ステージング・バッファから読み取ることができるXLAアプリケーションは1つのみです。

XLAでのその他のすべての手順(ブックマークに関連するものは除く)は、「XLAイベント・ハンドラ・アプリケーションの作成」で説明されている手順と同じです。

非永続モードでのXLAの初期化

「データ・ストア接続ハンドルの取得」の説明に従ってODBCを初期化し、環境ハンドル、接続ハンドルおよび文ハンドルを取得した後、非永続モードでXLAを初期化し、XLAハンドルを取得してトランザクション・ログにアクセスできます。 非永続モードでは、複数のオープンXLA接続が存在する場合がありますが、いずれの時点でも1つの接続のみがステージング・バッファにアクセスするように読取りを調整する必要があります。

非永続モードでのXLAの初期化は、ブックマークを識別する必要がないことを除き、永続モードでの初期化とほぼ同じです(「XLAの初期化およびXLAハンドルの取得」を参照)。 ttXlaHandle_h型としてXLAハンドルを初期化し、ttXlaOpenTimesTen関数へアドレスを渡してXLAハンドルを取得します。

ttXlaHandle_h xla_handle;
rc = ttXlaOpenTimesTen(hdbc, &xla_handle);

ステージング・バッファの構成

非永続モードでXLAを初期化した後、ttXlaConfigBuffer関数を使用して、XLAステージング・バッファのサイズを設定します。1つのステージング・バッファのみが1つのデータ・ストアに対して設定できます。ステージング・バッファのサイズの設定は、通常の切断の場合は保持されます。ただし、このサイズ設定は、チェックポイント処理が行われているかどうかによっては、異常終了した場合に保持されない可能性があります。

XLAの使用を終了した後、サイズを0(ゼロ)に設定してステージング・バッファを削除できます。

詳細は、「ttXlaConfigBuffer」を参照してください。

バッファ・ステータスの取得およびリセット

非永続モードでXLAを処理している場合は、ttXlaStatus関数を使用して、トランザクション・ログ・バッファおよびXLAステージング・バッファのステータス情報を取得できます。

詳細は、「ttXlaStatus」を参照してください。