18 連続問合せ通知(CQN)の使用

連続問合せ通知(CQN)を使用すると、問合せをオブジェクト変更通知用(デフォルト)または問合せ結果変更通知用としてアプリケーションからデータベースに登録できます。登録済の問合せで参照されるオブジェクトは、登録済オブジェクトです。

オブジェクト変更通知(OCN)に対する問合せが登録されると、その問合せで参照されるオブジェクトがトランザクションによって変更されてコミットされるたびに、問合せ結果が変更されたかどうかにかかわらず、データベースからアプリケーションに通知されます。

問合せ結果変更通知(QRCN)に対して問合せが登録されると、その問合せの結果がトランザクションによって変更されてコミットされるたびに、データベースからアプリケーションに通知されます。

CQN登録により、1つ以上の問合せのリストが通知タイプ(OCNまたはQRCN)および通知ハンドラに関連付けられます。CQN登録を作成するには、PL/SQLインタフェースまたはOracle Call Interface(OCI)を使用できます。PL/SQLインタフェースを使用する場合、通知ハンドラはサーバー側のPL/SQLストアド・プロシージャになり、OCIを使用する場合、通知ハンドラはクライアント側のCコールバック・プロシージャになります。

この章では、CQNの一般的な概念と、PL/SQL CQNインタフェースの使用方法について説明します。

トピック:

注意:

OCNおよびQRCNという用語は、通知タイプと通知自体の両方を指します。アプリケーションによりOCNに対する問合せが登録されると、データベースからアプリケーションにOCNが送信され、アプリケーションによりQRCNに対する問合せが登録されると、データベースからアプリケーションにQRCNが送信されます。

関連項目:

CQNのためのOCIの使用の詳細は、『Oracle Call Interfaceプログラマーズ・ガイド』を参照してください。

オブジェクト変更通知(OCN)の概要

アプリケーションでオブジェクト変更通知(OCN)に対する問合せが登録されると、その問合せに関連付けられているオブジェクトがトランザクションによって変更されてコミットされるたびに、問合せ結果が変更されたかどうかにかかわらず、データベースからアプリケーションにOCNが送信されます。

たとえば、アプリケーションで例18-1のOCNに対する問合せが登録され、EMPLOYEES表を変更するトランザクションがユーザーによってコミットされると、変更された行が問合せの条件句を満たしていない場合でも(DEPARTMENT_ID = 5の場合など)、データベースからアプリケーションにOCNが送信されます。

例18-1 変更通知として登録される問合せ

SELECT EMPLOYEE_ID, SALARY
FROM EMPLOYEES
WHERE DEPARTMENT_ID = 10;

問合せ結果変更通知(QRCN)の概要

注意:

QRCNを使用するには、データベースのCOMPATIBLE初期化パラメータが11.0.0以上であり、自動UNDO管理(AUM)が有効(デフォルト設定)である必要があります。

アプリケーションで問合せ結果変更通知(QRCN)に対する問合せが登録されると、その問合せの結果がトランザクションによって変更されてコミットされるたびに、データベースからアプリケーションにQRCNが送信されます。

たとえば、アプリケーションで例18-1のQRCNに対する問合せが登録されると、問合せ結果セットが変更された場合のみ、つまり、次のデータ操作言語(DML)文のいずれかがコミットされた場合にのみ、データベースからアプリケーションにQRCNが送信されます。

  • 問合せの条件句(DEPARTMENT_ID = 10)を満たす行のINSERTまたはDELETE

  • 問合せの条件句(DEPARTMENT_ID = 10)を満たしている行のEMPLOYEE_IDまたはSALARY列へのUPDATE

  • 行の値が10から10以外の値に変更され、その行が結果セットから削除される原因となったDEPARTMENT_ID列へのUPDATE

  • 行の値が10以外の値から10に変更され、その行が結果セットに追加される原因となったDEPARTMENT_ID列へのUPDATE

デフォルトの通知タイプはOCNです。QRCNの場合は、CQ_NOTIFICATION$_REG_INFOオブジェクトのQOSFLAGS属性にQOS_QUERYを指定します。

QRCNでは、保証モード(デフォルト)またはベストエフォート・モードを選択できます。

トピック:

関連項目:

保証モード

保証モードでは、誤検出はありません。つまり、問合せ結果セットの変更が保証される場合にのみ、データベースからアプリケーションにQRCNが送信されます。

たとえば、アプリケーションで例18-1のQRCNに対する問合せが登録され、従業員201が部門10に所属しており、次の文が実行されるとします。

UPDATE EMPLOYEES
SET SALARY = SALARY + 10
WHERE EMPLOYEE_ID = 201;

UPDATE EMPLOYEES
SET SALARY = SALARY - 10
WHERE EMPLOYEE_ID = 201;

COMMIT;

このトランザクションの各UPDATE文によって問合せ結果セットは変更されますが、各文を合計すると問合せ結果セットへの影響はないため、このトランザクションに対してはデータベースからアプリケーションにQRCNは送信されません。

保証モードでは、CQ_NOTIFICATION$_REG_INFOオブジェクトのQOSFLAGS属性にQOS_BEST_EFFORTではなくQOS_QUERYを指定します。

問合せの中には、保証モードのQRCNに対して複雑すぎるものがあります。

関連項目:

保証モードで登録可能な問合せの特性は、「保証モードでQRCNに対して登録可能な問合せ」を参照してください。

ベストエフォート・モード

保証モードでは複雑すぎる問合せの中には、ベストエフォート・モードでQRCNに対して登録できるものがあります。このモードでは、簡略バージョンの問合せがCQNによって作成および登録されます。

次の2つの例で、この動作を示します。

例: 保証モードのQRCNに対して複雑すぎる問合せ

例18-2の問合せは、集計関数SUMを含んでおり、保証モードのQRCNに対しては複雑すぎます。

例18-2 保証モードのQRCNに対して複雑すぎる問合せ

SELECT SUM(SALARY)
FROM EMPLOYEES
WHERE DEPARTMENT_ID = 20;

ベストエフォート・モードでは、この例の問合せの次のような簡略バージョンがCQNによって登録されます。

SELECT SALARY
FROM EMPLOYEES
WHERE DEPARTMENT_ID = 20;

元の問合せの結果が変更されるたびに、その簡略バージョンの結果も変更されるため、簡略化が原因で通知が失われることはありません。ただし、簡略バージョンの結果は元の問合せの結果が変更されなくても変更される場合があるため、簡略化によって誤検出が発生することがあります。

ベストエフォート・モードでは、データベースは次の処理を行います。

  • 通知関連の処理によるOLTPレスポンスのオーバーヘッドを次のように最小化します。

    • 単一表問合せの場合、データベースでは、どの列が変更されたか、および変更された行によってどの条件句が満たされたかに基づいて、問合せ結果が変更されたかどうかを判断します。

    • 複数表問合せ(結合)の場合、データベースでは、複数の表の間の主キー/外部キー制約関係を使用して、問合せ結果が変更されたかどうかを判断します。

  • DML文によって問合せ結果セットが変更されるたびに、最初のDML文によって行われた変更が後続のDML文によって無効になった場合でも、アプリケーションにQRCNを送信します。

ベストエフォート・モードでのオーバーヘッドを最小化により、CQNで簡略化されない問合せに対しても、誤検出がまれに発生します。たとえば、この例の問合せと、「保証モード」のトランザクションを想定します。ベストエフォート・モードでは、CQNによって問合せは簡略化されませんが、トランザクションによって誤検出が発生します。

例: 簡略バージョンによってオブジェクトが無効になる問合せ

一部のタイプの問合せは非常に簡略化されているため、無効化がオブジェクト・レベルで発生します。つまり、該当の問合せで参照されるいずれかのオブジェクトが変更されるたびに発生します。このような問合せの例としては、サポートされていない列型を使用する問合せや、副問合せを含んでいる問合せがあります。この問題を解決するには、元の問合せを書き換えます。

たとえば、例18-3の問合せは、副問合せを含んでいるため、保証モードのQRCNに対しては複雑すぎます。

例18-3 簡略バージョンによってオブジェクトが無効になる問合せ

SELECT SALARY
FROM EMPLOYEES
WHERE DEPARTMENT_ID IN (
  SELECT DEPARTMENT_ID
  FROM DEPARTMENTS
  WHERE LOCATION_ID = 1700
);

ベストエフォート・モードでは、この例の問合せがCQNによって次のように簡略化されます。

SELECT * FROM EMPLOYEES, DEPARTMENTS;

問合せの簡略化により、オブジェクトが無効になる場合があります。ただし、元の問合せを次のように書き換えると、その問合せを保証モードまたはベストエフォート・モードで登録できます。

SELECT SALARY
FROM EMPLOYEES, DEPARTMENTS
WHERE EMPLOYEES.DEPARTMENT_ID = DEPARTMENTS.DEPARTMENT_ID
  AND DEPARTMENTS.LOCATION_ID = 1700;

ベストエフォート・モードでのみ登録可能な問合せについては、「ベストエフォート・モードでのみQRCNに対して登録可能な問合せ」で説明します。

デフォルトのQRCNモードは保証モードです。ベストエフォート・モードの場合は、CQ_NOTIFICATION$_REG_INFOオブジェクトのQOSFLAGS属性にQOS_BEST_EFFORTを指定します。

通知を生成するイベント

通知を生成するイベントは次のとおりです。

DMLトランザクションのコミット

通知タイプがOCNの場合、1つ以上の登録済オブジェクトを変更するあらゆるDMLトランザクションで、コミット時に各オブジェクトに対して通知が1つ生成されます。

通知タイプがQRCNの場合、1つ以上の登録済問合せの結果を変更するあらゆるDMLトランザクションで、コミット時に通知が生成されます。この通知には、結果が変更された問合せのIDが含まれます。

どちらの通知タイプでも、通知には次の内容が含まれます。

  • 変更された各表の名前

  • 操作タイプ(INSERTUPDATEまたはDELETE)

  • 変更された各行のROWID(ROWIDオプションを使用して登録が作成され、変更された行の数があまり多くない場合)。

関連項目:

ROWIDオプション

DDL文のコミット

OCNとQRCNの両方について、次のデータ定義言語(DDL)文により、コミット時に通知が生成されます。

  • ALTER TABLE

  • TRUNCATE TABLE

  • FLASHBACK TABLE

  • DROP TABLE

注意:

通知タイプがOCNの場合、DROP TABLE文のコミットによってDROP NOTIFICATIONが生成されます。

削除された表に対する問合せのOCN登録は、その表(すでに存在しない表)から関連付けが解除されますが、登録自体は存続します。これらの登録のいずれかが削除された表以外のオブジェクトに関連付けられている場合、それらのオブジェクトに対する変更がコミットされると、通知は引き続き生成されます。削除された表にのみ関連付けられていた登録も存続し、その作成者は問合せ(およびその参照オブジェクト)をその登録に追加できます。

OCN登録は、問合せの登録時点におけるオブジェクトのバージョンと定義に基づきます。オブジェクトが削除されると、そのオブジェクトの登録の関連付けはオブジェクトから永続的に解除されます。オブジェクトが削除されたオブジェクトと同じ名前および同じスキーマで作成された場合、作成されたオブジェクトは削除されたオブジェクトに関連付けられていたOCN登録には関連付けられません。

通知タイプがQRCNの場合、次のようになります。

  • 通知には次の内容が含まれます。

    • 結果が変更された問合せのID

    • 変更された表の名前

    • DDL操作のタイプ

  • 登録済問合せを無効にするDDL操作の中には、その問合せを登録解除する原因となるものもあります。

    たとえば、次の問合せがQRCNに対して登録されるとします。

    SELECT COL1 FROM TEST_TABLE
      WHERE COL2 = 1;
    

    TEST_TABLEのスキーマは次のとおりとします。

    (COL1 NUMBER, COL2 NUMBER, COL3 NUMBER)
    

    この場合、次のDDL文により、コミット時に問合せが無効になり、問合せが登録から削除されます。

    ALTER TABLE DROP COLUMN COL2;

登録解除

OCNとQRCNの両方について、登録解除(データベースからの登録の削除)によって通知が生成されます。データベースで登録が削除される原因は次のとおりです。

  • タイムアウト

    問合せの登録時にTIMEOUTがゼロ以外の値で指定された場合、指定の時間間隔後にデータベースによって登録がパージされます。

    問合せの登録時にQOS_DEREG_NFYが指定された場合、最初の通知の生成後にデータベースによって登録がパージされます。

  • 権限の消失

    登録済問合せに関連付けられているオブジェクトに対する権限が消失し、通知タイプがOCNの場合、データベースによって登録がパージされます。(通知タイプがQRCNの場合は、データベースによってその問合せは登録から削除されますが、登録はパージされません。)

クライアント・アプリケーションによる明示的な登録解除の実行時には、通知は生成されません。

関連項目:

問合せの登録に必要な権限は、「CQN登録を作成するための前提条件」を参照してください。

グローバル・イベント

グローバル・イベントEVENT_STARTUPおよびEVENT_SHUTDOWNにより、通知が生成されます。

Oracle RAC環境では、次のイベントによって通知が生成されます。

  • EVENT_STARTUP(データベースの最初のインスタンスの起動時)

  • EVENT_SHUTDOWN(データベースの最終インスタンスの停止時)

  • EVENT_SHUTDOWN_ANY(データベースのいずれかのインスタンスの停止時)

前述のグローバル・イベントは、DBMS_CQ_NOTIFICATIONパッケージで定義されている定数です。

関連項目:

DBMS_CQ_NOTIFICATIONパッケージの詳細は、『Oracle Database PL/SQLパッケージおよびタイプ・リファレンス』を参照してください。

通知内容

通知には、次の情報の一部またはすべてが含まれます。

  • イベントのタイプ(次のいずれか)

    • 起動

    • オブジェクト変更

    • 問合せ結果変更

    • 登録解除

    • 停止

  • 影響を受けた登録の登録ID

  • 変更されたオブジェクトの名前

  • ROWIDオプションが指定されている場合の、変更された行のROWID

  • 通知タイプがQRCNの場合の、結果が変更された問合せの問合せID

  • 通知がDMLまたはDDL文の結果として生成された場合

    • 変更された表の名前の配列

    • 操作タイプ(INSERTUPDATEなど)

通知には、変更されたデータ自体は含まれません。たとえば、5000から6000への月給の増額があっても、通知には示されません。変更されたオブジェクト、行または問合せ結果の最新の値を取得するには、アプリケーションからデータベースに問い合せる必要があります。

CQNに適したアプリケーション

CQNに適したアプリケーションは、データベースへのネットワーク・ラウンドトリップを回避するために、変更の頻度の低いオブジェクトに対する問合せの結果セットを中間層にキャッシュするアプリケーションです。これらのアプリケーションでは、CQNを使用して、キャッシュされる問合せを登録できます。このようなアプリケーションは、通知を受信すると、登録済問合せを再実行してキャッシュをリフレッシュできます。

このようなアプリケーションの例としては、Webフォーラムがあります。ユーザーは、コンテンツがデータベースに挿入されても、挿入後すぐにそのコンテンツを参照する必要はないため、このアプリケーションでは情報を中間層にキャッシュし、キャッシュをいつリフレッシュするかをCQNで指示できます。

図18-1に、データがデータベースから提供され、中間にキャッシュされた後、インターネット経由でアクセスされる一般的な使用例を示します。

図18-1 中間層キャッシュ

図18-1の説明が続きます
「図18-1 中間層キャッシュ」の説明

中間層のアプリケーションは、キャッシュをデータベースと相対でできるかぎり最新の状態に保ちつつ、データベース・オブジェクトのキャッシュ・コピーに迅速にアクセスする必要があります。キャッシュ・データは、トランザクションでデータが変更されてコミットされると不要になるため、アプリケーションが不正な結果にアクセスする危険性があります。アプリケーションがCQNを使用している場合、データベースは、登録済オブジェクトに変更が発生した時点で変更内容の詳細を使用して通知を公開できます。この通知に応答して、アプリケーションではキャッシュ・データをバックエンド・データベースからフェッチしてリフレッシュできます。

図18-2に、中間層のWebクライアントが通知を受信して処理するプロセスを示します。

図18-2 連続問合せ通知(CQN)の基本プロセス

図18-2の説明が続きます
「図18-2 連続問合せ通知(CQN)の基本プロセス」の説明

図18-2の各ステップは次のとおりです(PL/SQLを使用して登録が作成され、アプリケーションによりHR.EMPLOYEESに対する問合せの結果セットがキャッシュされている場合)。

  1. 開発者は、PL/SQLを使用して、問合せに対するCQN登録を作成します。このプロセスでは、通知を処理するストアドPL/SQLプロシージャを作成した後、PL/SQL CQNインタフェースを使用して問合せに対する登録を作成し、作成したPL/SQLプロシージャを通知ハンドラとして指定します。

  2. データベースでは、データ辞書に登録情報が移入されます。

  3. ユーザーがバックエンド・データベースのHR.EMPLOYEES表の行を更新し、その更新をコミットすると、問合せ結果が変更されます。これで、中間層でキャッシュされていたHR.EMPLOYEESのデータが無効になります。

  4. データベースにより、内部キューに変更を説明するメッセージが追加されます。

  5. データベースからJOBQバックグラウンド・プロセスに、通知メッセージが通知されます。

  6. JOBQプロセスにより、クライアント・アプリケーションから指定されたストアド・プロシージャが実行されます。この例では、JOBQからサーバー側PL/SQLプロシージャにデータが渡されます。PL/SQL通知ハンドラの実装により、通知の処理方法が決まります。

  7. 開発者は、サーバー側PL/SQLプロシージャ内に、登録済オブジェクトに対する変更を中間層のクライアント・アプリケーションに通知するための論理を実装できます。たとえば、HR.EMPLOYEES内で変更された行のROWIDをアプリケーションに通知します。

  8. 中間層のクライアント・アプリケーションでは、バックエンド・データベースを問い合せて、変更があった行のデータを取得します。

  9. クライアント・アプリケーションにより、キャッシュがこのデータで更新されます。

CQN登録の作成

CQN登録により、1つ以上の問合せのリストが通知タイプおよび通知ハンドラに関連付けられます。

通知タイプは、OCNまたはQRCNです。

CQN登録を作成するには、2つのインタフェースのいずれかを使用します。

  • PL/SQLインタフェース

    PL/SQLインタフェースを使用する場合、通知ハンドラはサーバー側PL/SQLストアド・プロシージャです。

  • Oracle Call Interface(OCI)

    OCIを使用する場合、通知ハンドラはクライアント側Cコールバック・プロシージャです。

作成された登録は、データベースに格納されます。Oracle RAC環境では、この登録はすべてのデータベース・インタフェースから参照できます。いずれかのデータベース・インスタンスの問合せ結果がトランザクションによって変更されると、通知が生成されます。

デフォルトでは、登録は、それを作成したアプリケーションによって明示的に登録解除されるまで、または(権限の消失などが原因で)データベースによって暗黙的にパージされるまで存続します。

PL/SQLを使用したCQN登録の作成

この項では、PL/SQLを使用したCQN登録の作成について説明します。PL/SQLインタフェースを使用する場合、通知ハンドラはサーバー側PL/SQLストアド・プロシージャです。

トピック:

PL/SQL CQN登録インタフェース

PL/SQL CQN登録インタフェースは、DBMS_CQ_NOTIFICATIONパッケージを使用して実装されます。登録ブロックを開くには、DBMS_CQ_NOTIFICATION.NEW_REG_STARTファンクションを使用します。通知タイプや通知ハンドラなどの登録詳細は、CQ_NOTIFICATION$_REG_INFOオブジェクトの一部として指定し、このオブジェクトは引数としてNEW_REG_STARTプロシージャに渡されます。登録ブロックがオープンの間に実行したすべての問合せは、CQNに登録されます。通知タイプとしてQRCNを指定すると、データベースによって各問合せに問合せIDが割り当てられます。この問合せIDは、DBMS_CQ_NOTIFICATION.CQ_NOTIFICATION_QUERYIDファンクションを使用して取得できます。登録ブロックをクローズするには、DBMS_CQ_NOTIFICATION.REG_ENDファンクションを使用します。

関連項目:

CQN登録オプション

CQN登録のデフォルトは、表18-1に示すオプションを使用して変更できます。

表18-1 連続問合せ通知登録オプション

オプション 説明:

通知のタイプ

QRCNを指定します(デフォルトはOCN)。

QRCNモード脚注1

ベストエフォート・モードを指定します(デフォルトは保証モード)。

ROWID

変更された各行のROWID疑似列の値を通知に含めます。

操作フィルタ脚注2

指定したフィルタ条件を操作タイプが満たした場合のみ、通知を公開します。

トランザクション・ラグ脚注2

非推奨。かわりに通知グループ化を使用してください。

通知グループ化

通知のグループ化方法を指定します。

信頼性

通知を(デフォルトの共有メモリーではなく)永続データベース・キューに格納します。

通知時にパージ

最初の通知後に登録をパージします。

タイムアウト

指定の時間間隔後に登録をパージします。

脚注1

通知タイプがQRCNの場合にのみ適用されます。

脚注2

通知タイプがOCNの場合にのみ適用されます。

トピック:

「通知タイプ」オプション

通知タイプは、OCNおよびQRCNです。

QRCNモード(QRCN通知タイプのみ)

QRCNモード・オプションは、通知タイプがQRCNの場合にのみ適用されます。通知タイプをQRCNに設定する手順は、「「通知タイプ」オプション」を参照してください。

次に、QRCNモードを示します。
  • 保証

  • ベスト・エフォート

デフォルトは保証モードです。ベストエフォート・モードの場合は、CQ_NOTIFICATION$_REG_INFOオブジェクトのQOSFLAGS属性にQOS_BEST_EFFORTを指定します。

ROWIDオプション

ROWIDオプションにより、変更された行それぞれのROWID疑似列の値(その行のrowid)が通知に含まれるようになります。変更された各行のROWIDオプションを通知に含めるには、CQ_NOTIFICATION$_REG_INFOオブジェクトのQOSFLAGS属性にQOS_ROWIDSを指定します。

注意:

ハイブリッド列圧縮(HCC)で圧縮された表の行を更新すると、行のROWIDが変更されます。特定のOracleストレージ・システムの機能であるHCCの詳細は、『Oracle Database概要』を参照してください。

アプリケーションでは、次の形式の問合せを実行すると、変更された行の内容を通知のROWID情報から取得できます。

SELECT * FROM table_name_from_notification
WHERE ROWID = rowid_from_notification;

ROWIDは、外部文字列形式で公開されます。通常のヒープ表では、ROWIDの長さは18バイトです。索引構成表(IOT)では、ROWIDの長さは主キーのサイズによって異なり、18バイトより長い場合があります。

ROWIDに十分なメモリーがサーバーにない場合、通知はFULL-TABLE-NOTIFICATIONにロールアップされることがあります(これは、通知記述子内の特殊フラグで示されます)。FULL-TABLE-NOTIFICATIONに対して考えられる原因は次のとおりです。

  • ROWIDによって消費される共有メモリーの合計が動的共有プール・サイズの1%を超えています。

  • 1回のトランザクションで1つの登録済オブジェクト内で変更された行が多すぎます(上限は約80行)。

  • IOTについて変更された行の論理ROWIDの合計長が長すぎます(上限は約1800バイト)。

  • 通知グループ化オプションNTFN_GROUPING_TYPEに値DBMS_CQ_NOTIFICATION.NTFN_GROUPING_TYPE_SUMMARYが指定されています(「通知グループ化オプション」を参照)。

FULL-TABLE-NOTIFICATIONにはROWIDは含まれないため、これを受信したアプリケーションでは表全体(つまり、すべての行)が変更された可能性があるとみなされます。

操作フィルタ・オプション(OCN通知タイプのみ)

操作フィルタ・オプションは、通知タイプがOCNの場合にのみ適用されます。

操作フィルタ・オプションを使用すると、通知を生成する操作のタイプを指定できます。

デフォルトは全操作です。一部の操作でのみ通知が生成されるように指定するには、CQ_NOTIFICATION$_REG_INFOオブジェクトのOPERATIONS_FILTER属性を使用します。OPERATIONS_FILTER属性では、操作のタイプを表す定数を使用してタイプを指定し、この定数は、DBMS_CQ_NOTIFICATIONパッケージで次のように定義されています。

演算子 定数

INSERT

DBMS_CQ_NOTIFICATION.INSERTOP

UPDATE

DBMS_CQ_NOTIFICATION.UPDATEOP

DELETE

DBMS_CQ_NOTIFICATION.DELETEOP

ALTEROP

DBMS_CQ_NOTIFICATION.ALTEROP

DROPOP

DBMS_CQ_NOTIFICATION.DROPOP

UNKNOWNOP

DBMS_CQ_NOTIFICATION.UNKNOWNOP

すべて(デフォルト)

DBMS_CQ_NOTIFICATION.ALL_OPERATIONS

複数の操作を指定するには、ビット単位のORを使用します。例:

DBMS_CQ_NOTIFICATION.INSERTOP + DBMS_CQ_NOTIFICATION.DELETEOP

QOS_QUERYは通知タイプとしてQRCNを指定するため、QOSFLAGS属性にQOS_QUERYも指定した場合はOPERATIONS_FILTERは無効です。

関連項目:

DBMS_CQ_NOTIFICATIONパッケージの詳細は、『Oracle Database PL/SQLパッケージおよびタイプ・リファレンス』を参照してください。

トランザクション・ラグ・オプション(OCN通知タイプのみ)

トランザクション・ラグ・オプションは、通知タイプがOCNの場合にのみ適用されます。

注意:

このオプションは非推奨です。フロー制御の通知を実装するには、「通知グループ化オプション」を使用してください。

トランザクション・ラグ・オプションでは、クライアント・アプリケーションがデータベースから遅れることのできるトランザクション数を指定します。数が0の場合、登録済オブジェクトを変更するトランザクションごとに通知が送信されます。数が5の場合、登録済オブジェクトを変更するトランザクションのうち5番目ごとに通知が送信されます。データベースでは、クライアントで中間の変更が失われないように、中間の変更をオブジェクトの粒度で追跡して通知に含めます。

0より大きいトランザクション・ラグが役立つのは、アプリケーションでフロー制御の通知を実装している場合のみです。アプリケーションでは、通知がラグに達する程度に十分な頻度で生成されるようにし、無限に遅延されないようにしてください。

TRANSACTION_LAGを指定すると、QOS_ROWIDSも指定した場合でも、通知にROWIDは含まれません。

通知グループ化オプション

デフォルトでは、通知はその原因となるイベントの直後に生成されます。

通知グループ化オプションは、CQ_NOTIFICATION$_REG_INFOオブジェクトの属性であり、次の種類があります。

属性 説明:

NTFN_GROUPING_CLASS

通知をグループ化するクラスを指定します。使用可能な値は、DBMS_CQ_NOTIFICATION.NTFN_GROUPING_CLASS_TIME(通知を時間でグループ化)と、デフォルトのゼロ(通知をその原因となるイベントの直後に生成)のみです。

NTFN_GROUPING_VALUE

グループを定義する時間間隔を秒単位で指定します。たとえば、この値が900の場合、同じ15分間隔内に生成された通知がグループ化されます。

NTFN_GROUPING_TYPE

次のいずれかのグループ化のタイプを指定します。

  • DBMS_CQ_NOTIFICATION.NTFN_GROUPING_TYPE_SUMMARY: グループ内のすべての通知は、単一の通知に要約されます。

    注意: ROWIDオプションを指定した場合でも、この単一の通知にはROWIDは含まれません。

  • DBMS_CQ_NOTIFICATION.NTFN_GROUPING_TYPE_LAST: グループ内の最後の通知のみが公開され、それより前の通知は破棄されます。

NTFN_GROUPING_START_TIME

通知生成の開始時間を指定します。NULLが指定されると、システム生成の現在の時間がデフォルトになります。

NTFN_GROUPING_REPEAT_COUNT

通知を繰り返す回数を指定します。登録の存続中に通知を永続的に受信するには、DBMS_CQ_NOTIFICATION.NTFN_GROUPING_FOREVERに設定します。登録の存続中に最大でnの通知を受信するには、nに設定します。

注意:

タイムアウト、権限の消失およびグローバル・イベントによって生成された通知は、指定したグループ化間隔が終了する前に公開される場合があります。この場合、グループ化された保留中の通知が存在すると、間隔が終了する前にその通知も公開されます。

信頼可能オプション

デフォルトでは、CQN登録は共有メモリーに格納されます。これをかわりに永続データベース・キューに格納するには(つまり、信頼できる通知を生成するには)、CQ_NOTIFICATION$_REG_INFOオブジェクトのQOSFLAGS属性にQOS_RELIABLEを指定します。

信頼できる通知のメリットは、その生成後にデータベースで障害が発生しても、再起動後にその通知を配信できることです。Oracle RAC環境では、正常なデータベース・インスタンスがこの通知を配信できます。

信頼できる通知のデメリットは、デフォルトの通知よりもCPUおよびI/Oのコストが高いことです。

「通知時にパージ」および「タイムアウト」オプション

デフォルトでは、CQN登録は、それを作成したアプリケーションによって明示的に登録解除されるまで、または(権限の消失などが原因で)データベースによって暗黙的にパージされるまで存続します。

登録の最初の通知の生成後にその登録をパージするには、CQ_NOTIFICATION$_REG_INFOオブジェクトのQOSFLAGS属性にQOS_DEREG_NFYを指定します。

n秒後に登録をパージするには、CQ_NOTIFICATION$_REG_INFOオブジェクトのTIMEOUT属性にnを指定します。

「通知時にパージ」と「タイムアウト」の2つのオプションは、同時に使用できます。

CQN登録を作成するための前提条件

CQN登録を作成するための前提条件は次のとおりです。

  • 次の権限が必要です。

    • DBMS_CQ_NOTIFICATIONパッケージのEXECUTE権限(登録の作成にこのパッケージのサブプログラムを使用)

    • CHANGE NOTIFICATIONシステム権限

    • 登録する各オブジェクトでのREADまたはSELECT権限

    登録済の問合せに関連付けられているオブジェクトに対する権限を失うと、通知が生成されます。

  • SYS以外のユーザーとして接続する必要があります。

  • コミットされていないトランザクションの途中でない必要があります。

  • dml_locks init.oraパラメータの値がゼロ以外である必要があります(デフォルト値はゼロ以外です)。

    (これは、通知を受信するための前提条件でもあります。)

注意:

QRCNを使用するには、データベースのCOMPATIBLE設定が11.0.0以上である必要があります。

関連項目:

登録解除

オブジェクト変更通知(OCN)に対して登録可能な問合せ

ストアド・プロシージャやREFカーソルの一部として実行される問合せなど、ほとんどの問合せをOCNに対して登録できます。

OCNに対して登録不可能な問合せは次のとおりです。

  • 固定表または固定ビューに対する問合せ

  • ユーザー・ビューに対する問合せ

  • データベース・リンク(dblink)を含む問合せ

  • マテリアライズド・ビューでの問合せ

注意:

OCN登録ではシノニムを使用できますが、QRCN登録では使用できません。

問合せ結果変更通知(QRCN)に対して登録可能な問合せ

QRCNに対して保証モードで登録可能な問合せ、QRCNに対してベストエフォート・モードでのみ登録可能な問合せ、およびQRCNに対してどちらのモードでも登録不可能な問合せがあります。

トピック:

保証モードでQRCNに対して登録可能な問合せ

問合せを保証モードでQRCNに対して登録するには、問合せを次のルールに準拠させる必要があります。

  • 参照するすべての列がNUMBERまたはVARCHAR2データ型である必要があります。

  • 列の式に含まれる算術演算子が次のバイナリ演算子に制限され、そのオペランドが数値データ型の列である必要があります。

    • + (加算)

    • -(減算、単項マイナスではない)

    • * (乗算)

    • / (除算)

  • 条件句の比較演算子は次の演算子に制限されます。

    • < (未満)

    • <=(以下)

    • = (等しい)

    • >=(以上)

    • > (より大きい)

    • <>または!=(等しくない)

    • IS NULL

    • IS NOT NULL

  • 条件句のBoolean演算子はANDORおよびNOTに制限されます。

  • 問合せに集計関数(SUMCOUNTAVERAGEMINMAXなど)は含められません。

保証モードでは、単一表および一部の内部等価結合に対するほとんどの問合せが可能です(次の例を参照)。

SELECT SALARY FROM EMPLOYEES, DEPARTMENTS
  WHERE EMPLOYEES.DEPARTMENT_ID = DEPARTMENTS.DEPARTMENT_ID
    AND DEPARTMENTS.LOCATION_ID = 1700;

注意:

  • 問合せオプティマイザで使用される実行計画により、問合せが保証モードでは矛盾する場合があります(OR拡張など)。

  • 保証モードで登録可能な問合せは、ベストエフォート・モードでも登録可能ですが、ベストエフォート・モードではCQNで簡略化されない問合せに対しても誤検出が発生する可能性があるため、結果は異なる場合があります。

関連項目:

ベストエフォート・モードでのみQRCNに対して登録可能な問合せ

次のいずれかの条件を満たす問合せは、ベストエフォート・モードでのみQRCNに対して登録可能であり、その簡略バージョンでは通知がオブジェクトの粒度で生成されます。

  • 暗号化が有効にされている列を参照する問合せ

  • 同じ型の項目がSELECT構文のリストに11以上ある問合せ

  • 次のいずれかを使用した式を含む問合せ

    • 文字列関数(SUBSTRLTRIMRTRIMなど)

    • 算術関数(TRUNCABSSQRTなど)

    • パターン一致条件LIKEおよびREGEXP_LIKE

    • EXISTSまたはNOT EXISTS条件

  • 別の表の列で定義されている条件句を使用した論理和を含む問合せ。例:

    SELECT EMPLOYEE_ID, DEPARTMENT_ID
      FROM EMPLOYEES, DEPARTMENTS
        WHERE EMPLOYEES.EMPLOYEE_ID = 10
          OR DEPARTMENTS.DEPARTMENT_ID = 'IT';
    
  • ユーザーROWIDアクセスを含む問合せ。例:

    SELECT DEPARTMENT_ID
      FROM DEPARTMENTS
        WHERE ROWID = 'AAANkdAABAAALinAAF';
    
  • 内部結合以外の結合を含む問合せ

  • 次のいずれかを使用した実行計画を含む問合せ

    • ビットマップ結合索引、ドメイン索引またはファンクション索引

    • UNION ALLまたはCONCATENATION

      (問合せ自体の内部、または問合せオプティマイザで選択されたOR拡張実行計画の結果内で使用。)

    • ORDER BYまたはGROUP BY

      (問合せ自体の内部、または問合せオプティマイザで選択された実行計画内のORDER BYオプションを使用したSORT操作の結果内で使用。)

    • オーバーフロー・セグメントがあり、パーティション化された索引構成表(IOT)

    • クラスタ・オブジェクト

    • パラレル実行

関連項目:

SQL関数のリストは、『Oracle Database SQL言語リファレンス』を参照してください。

どちらのモードでもQRCNに対して登録不可能な問合せ

次のいずれかを参照する問合せは、保証モードとベストエフォート・モードのどちらでもQRCNに対して登録不可能です。

  • ビュー

  • 固定表、リモート表または仮想プライベート・データベース(VPD)ポリシーが有効化されている表

  • DUAL(SELECT構文のリスト内)

  • シノニム

  • ユーザー定義のPL/SQLサブプログラムのコール

  • 「保証モードでQRCNに対して登録可能な問合せ」にリストされていない演算子

  • 集計関数COUNT

    (他の集計関数は、ベストエフォート・モードでは登録可能ですが、保証モードでは登録不可能です。)

  • アプリケーション・コンテキスト(次の例を参照)

    SELECT SALARY FROM EMPLOYEES
    WHERE USER = SYS_CONTEXT('USERENV', 'SESSION_USER');
    
  • SYSDATESYSTIMESTAMPまたはCURRENT TIMESTAMP

また、マテリアライズド・ビューを使用して問合せオプティマイザがリライトした問合せはQRCNに対して登録できません。

関連項目:

問合せオプティマイザの詳細は、『Oracle Database SQLチューニング・ガイド』を参照してください。

PL/SQLを介したCQNに対する問合せの登録

PL/SQLを使用してCQN登録を作成するステップは、次のとおりです。

  1. 通知ハンドラとして機能するストアドPL/SQLプロシージャを作成します。
    「PL/SQL通知ハンドラの作成」を参照してください。
  2. 通知ハンドラの名前、通知タイプ、および登録のその他の属性を指定するCQ_NOTIFICATION$_REG_INFOオブジェクトを作成します。
  3. クライアント・アプリケーションで、DBMS_CQ_NOTIFICATION.NEW_REG_STARTファンクションを使用して登録ブロックをオープンします。

    CQ_NOTIFICATION$_REG_INFOオブジェクトとファンクションNEW_REG_STARTおよびREG_ENDの詳細は、『Oracle Database PL/SQLパッケージ・プロシージャおよびタイプ・リファレンス』を参照してください。それらすべてはDBMS_CQ_NOTIFICATIONパッケージで定義されています。

  4. 登録する問合せを実行します(DML操作またはDDL操作は実行しないでください)。
    詳細は次のトピックを参照してください。
  5. DBMS_CQ_NOTIFICATION.REG_ENDファンクションを使用して登録ブロックをクローズします。
PL/SQL通知ハンドラの作成

通知ハンドラとして使用するPL/SQLストアド・プロシージャの作成時には、次のシグネチャを含める必要があります。

PROCEDURE schema_name.proc_name(ntfnds IN CQ_NOTIFICATION$_DESCRIPTOR)

このシグネチャで、schema_nameはデータベース・スキーマ名、proc_nameはストアド・プロシージャ名、ntfndsは通知記述子を示します。

通知記述子はCQ_NOTIFICATION$_DESCRIPTORオブジェクトであり、その属性によって変更の詳細(トランザクションID、変更のタイプ、影響を受けた問合せ、変更された表など)が記述されます。

通知記述子ntfndsJOBQプロセスから通知ハンドラproc_nameに渡され、このハンドラがアプリケーション要件に従って通知を処理します。(これは図18-2のステップ6に示します。)

注意:

通知ハンドラは、ジョブ・キュー・プロセス内で実行されます。JOB_QUEUE_PROCESSES初期化パラメータは、ジョブの実行用に作成できる最大プロセス数を指定します。PL/SQL通知を受信するには、JOB_QUEUE_PROCESSESをゼロ以外の値に設定する必要があります。

関連項目:

JOB_QUEUE_PROCESSES

CQ_NOTIFICATION$_REG_INFOオブジェクトの作成

タイプCQ_NOTIFICATION$_REG_INFOのオブジェクトでは、登録済オブジェクトの変更時にデータベースで実行される通知ハンドラを指定します。SQL*Plusでは、次の文を実行してそのタイプ属性を表示できます。

DESC CQ_NOTIFICATION$_REG_INFO

表18-2に、SYS.CQ_NOTIFICATION$_REG_INFOの属性を示します。

表18-2 CQ_NOTIFICATION$_REG_INFOの属性

属性 説明:

CALLBACK

通知の生成時に実行されるPL/SQLプロシージャ(通知ハンドラ)の名前を指定します。この名前は、hr.dcn_callbackのようにschema_name.procedure_name形式で指定する必要があります。

QOSFLAGS

1つ以上のサービスのクオリティ・フラグ(DBMS_CQ_NOTIFICATIONパッケージ内の定数)を指定します。この名前と説明は、表18-3を参照してください。

複数のサービスのクオリティ・フラグを指定するには、ビット単位のORを使用します。たとえば、DBMS_CQ_NOTIFICATION.QOS_RELIABLE + DBMS_CQ_NOTIFICATION.QOS_ROWIDSなどです。

TIMEOUT

登録のタイムアウト期間を指定します。0(ゼロ)以外の値に設定する場合は、データベースにより登録がパージされるまでの秒数を指定します。0またはNULLの場合、登録はクライアントにより明示的に解除されるまで存続します。

QOSFLAGS属性およびそのQOS_DEREG_NFYフラグと併用できます。

OPERATIONS_FILTER

OCN(「オブジェクト変更通知(OCN)の概要」を参照)にのみ適用されます。QOS_FLAGS属性にQOS_QUERYフラグが指定されている場合は無効です。

SQL文の型に基づいてメッセージをフィルタリングします。DBMS_CQ_NOTIFICATIONパッケージ内の次の定数を指定できます。

  • ALL_OPERATIONS。すべての変更時に通知します。

  • INSERTOP。INSERT時に通知します。

  • UPDATEOP。UPDATE時に通知します。

  • DELETEOP。DELETE時に通知します。

  • ALTEROPALTER TABLE操作時に通知します。

  • DROPOPDROP TABLE操作時に通知します。

  • UNKNOWNOP。不明な操作時に通知します。

ビット単位のORを使用すると、操作の組合せを指定できます。たとえば、DBMS_CQ_NOTIFICATION.INSERTOP + DBMS_CQ_NOTIFICATION.DELETEOPなどです。

TRANSACTION_LAG

非推奨。フロー制御の通知を実装するには、NTFN_GROUPING_*属性を使用してください。

OCN(「オブジェクト変更通知(OCN)の概要」を参照)にのみ適用されます。QOS_FLAGS属性にQOS_QUERYフラグが指定されている場合は無効です。

クライアントがデータベースから遅れることのできるトランザクション数またはデータベース変更数を指定します。0の場合、クライアントは無効化メッセージを生成直後に受信します。5の場合、登録済オブジェクトを変更するトランザクションのうち5番目ごとに通知が送信されます。データベースでは、中間の変更がオブジェクトの粒度で追跡され、変更が通知とともにバンドルされます。そのため、クライアントから中間の変更が失われることはありません。

トランザクションのコミット時にオブジェクトの変更通知をそれ以上の遅延なしで受信する必要のあるほとんどのアプリケーションでは、トランザクション・ラグ0(ゼロ)を選択するように期待されます。ゼロ以外のトランザクション・ラグが役立つのは、アプリケーションで通知に対するフロー制御を実装している場合のみです。ゼロ以外のトランザクション・ラグを使用する場合は、アプリケーションのワークロードのプロパティを、通知が妥当な頻度で生成されるように設定することをお薦めします。そうしないと、ラグに達するまで通知が無限に遅延する可能性があります。

TRANSACTION_LAGを指定すると、登録時にQOS_ROWIDSを指定した場合にも、通知メッセージにはROWIDレベルの粒度を使用できなくなります。

NTFN_GROUPING_CLASS

通知をグループ化するクラスを指定します。使用可能な値はDBMS_CQ_NOTIFICATION.NTFN_GROUPING_CLASS_TIME(通知を時間でグループ化)のみです。

NTFN_GROUPING_VALUE

グループを定義する時間間隔を秒単位で指定します。たとえば、この値が900の場合、同じ15分間隔内に生成された通知がグループ化されます。

NTFN_GROUPING_TYPE

次のいずれかのグループ化のタイプを指定します。

  • DBMS_CQ_NOTIFICATION.NTFN_GROUPING_TYPE_SUMMARY: グループ内のすべての通知は、単一の通知に要約されます。

  • DBMS_CQ_NOTIFICATION.NTFN_GROUPING_TYPE_LAST: グループ内の最後の通知のみが公開され、それより前の通知は破棄されます。

NTFN_GROUPING_START_TIME

通知生成の開始時間を指定します。NULLが指定されると、システム生成の現在の時間がデフォルトになります。

NTFN_GROUPING_REPEAT_COUNT

通知を繰り返す回数を指定します。登録の存続中に通知を永続的に受信するには、DBMS_CQ_NOTIFICATION.NTFN_GROUPING_FOREVERに設定します。登録の存続中に最大でnの通知を受信するには、nに設定します。

表18-3のサービスのクオリティ・フラグは、DBMS_CQ_NOTIFICATIONパッケージ内の定数です。このフラグは、CQ_NOTIFICATION$_REG_INFOQOS_FLAGS属性を使用して指定できます(表18-2 を参照)。

表18-3 サービスのクオリティ・フラグ

フラグ 説明:

QOS_DEREG_NFY

最初の通知後に登録をパージします。

QOS_RELIABLE

通知を永続データベース・キューに格納します。

Oracle RAC環境では、データベース・インスタンスで障害が発生すると、正常なデータベース・インスタンスがキューにある通知メッセージを配信できます。

デフォルト: 通知はより効率のよい共有メモリーに格納されます。

QOS_ROWIDS

変更された各行のROWIDを通知に含めます。

QOS_QUERY

QRCNに対する問合せを登録します(「問合せ結果変更通知(QRCN)の概要」を参照)。

QRCNに対する問合せを登録できない場合は、QOS_BEST_EFFORTも指定していないかぎり、登録時にエラーが生成されます。

デフォルト: OCNに対する問合せが登録されます(「オブジェクト変更通知(OCN)の概要」を参照)

QOS_BEST_EFFORT

QOS_QUERYと併用されます。問合せ結果変更の評価には複雑すぎる問合せの簡略バージョンを登録します。つまり、QRCNに対する問合せをベストエフォート・モードで登録します(「ベストエフォート・モード」を参照)。

どの問合せが簡略化されているかを確認するには、静的データ・ディクショナリ・ビューDBA_CQ_NOTIFICATION_QUERIESまたはUSER_CQ_NOTIFICATION_QUERIESを問い合せます。これらのビューでは、登録済の各問合せのQUERYIDとテキストが表示されます。

デフォルト: QRCNに対して問合せが保証モードで登録されます(「保証モード」を参照)

登録済オブジェクトに変更があるたびに、プロシージャHR.dcn_callbackを起動する必要があるとします。例18-4では、HR.dcn_callbackが通知を受け取るように指定するCQ_NOTIFICATION$_REG_INFOオブジェクトを作成します。このオブジェクトを作成するには、DBMS_CQ_NOTIFICATIONパッケージに対するEXECUTE権限が必要です。

例18-4 CQ_NOTIFICATION$_REG_INFOオブジェクトの作成

DECLARE
  v_cn_addr CQ_NOTIFICATION$_REG_INFO;

BEGIN
  -- Create object:

  v_cn_addr := CQ_NOTIFICATION$_REG_INFO (
    'HR.dcn_callback',                 -- PL/SQL notification handler
    DBMS_CQ_NOTIFICATION.QOS_QUERY     -- notification type QRCN
    + DBMS_CQ_NOTIFICATION.QOS_ROWIDS, -- include rowids of changed objects
    0,                          -- registration persists until unregistered
    0,                          -- notify on all operations
    0                           -- notify immediately
    );

  -- Register queries: ...
END;
/
通知内の各問合せの識別

問合せの登録済リストに含まれる問合せにより、連続問合せ通知が生成される場合があります。特定の問合せによっていつ通知が生成されるかを確認するには、その問合せのSELECT構文のリストでDBMS_CQ_NOTIFICATION.CQ_NOTIFICATION_QUERYIDファンクションを使用します。例:

SELECT EMPLOYEE_ID, SALARY, DBMS_CQ_NOTIFICATION.CQ_NOTIFICATION_QUERYID
FROM EMPLOYEES
WHERE DEPARTMENT_ID = 10;

結果:

EMPLOYEE_ID     SALARY CQ_NOTIFICATION_QUERYID
----------- ---------- -----------------------
        200       2800                       0
 
1 row selected.

問合せによって通知が生成される場合、その通知には問合せIDが含まれます。

既存の登録への問合せの追加

問合せを既存の登録に追加するステップは、次のとおりです。

  1. 既存の登録の登録IDを取得します。

    これは、保存済の出力または*_CHANGE_NOTIFICATION_REGSの問合せから取得できます。

  2. 登録IDをパラメータとして使用し、プロシージャDBMS_CQ_NOTIFICATION.ENABLE_REGをコールして既存の登録をオープンします。
  3. 登録する問合せを実行します(DML操作またはDDL操作は実行しないでください)。
  4. DBMS_CQ_NOTIFICATION.REG_ENDファンクションを使用して登録をクローズします。

    例18-5では、登録IDが21の既存の登録に問合せを追加します。

例18-5 既存の登録への問合せの追加

DECLARE
  v_cursor SYS_REFCURSOR;

BEGIN
  -- Open existing registration
  DBMS_CQ_NOTIFICATION.ENABLE_REG(21);
  OPEN v_cursor FOR
    -- Run query to be registered
    SELECT DEPARTMENT_ID
      FROM HR.DEPARTMENTS;  -- register this query
  CLOSE v_cursor;
  -- Close registration
  DBMS_CQ_NOTIFICATION.REG_END;
END;
/

CQN登録のベスト・プラクティス

CQNに最適なパフォーマンスを得るには、次の登録ガイドラインに従ってください。

  • 登録する問合せの数を少なくします。可能であれば、ほとんど変更されないオブジェクトを参照する問合せを登録します。

    オブジェクトの揮発性が非常に高いと、多数の通知が送信されることになり、そのオーバーヘッドによってOLTPスループットが低下します。

  • 同じ通知メッセージが複数の受信者宛に複製されないように、特定のオブジェクトについて重複登録の数を最小限に抑えます。

CQN登録のトラブルシューティング

登録を作成できない場合、または登録を作成しても予期した通知を受信していない場合は、次のような原因が考えられます。

  • JOB_QUEUE_PROCESSESパラメータがゼロ以外の値に設定されていません。

    このため、通知ハンドラを介してPL/SQL通知を受信できません。

  • 登録の作成時にSYSユーザーとして接続しました。

    CQN登録を作成するには、SYS以外のユーザーとして接続する必要があります。

  • 登録済オブジェクトを変更した後で、そのトランザクションをコミットしていません。

    通知は、トランザクションのコミット時にのみ生成されます。

  • 登録がデータベースに正常に作成されていません。

    確認するには、静的データ・ディクショナリ・ビュー*_CHANGE_NOTIFICATION_REGSを問い合せます。たとえば、次の文により、現行ユーザーに関するすべての登録と登録済オブジェクトが表示されます。

    SELECT REGID, TABLE_NAME FROM USER_CHANGE_NOTIFICATION_REGS;
    
  • 通知ハンドラの実行中に実行時エラーが発生しました。

    その場合は、プロシージャの実行を試行したJOBQプロセスのトレース・ファイルに記録されます。通常、トレース・ファイルの名前は次の形式になります。

    ORACLE_SID_jnumber_PID.trc
    

    たとえば、ORACLE_SIDがdbs1で、JOBQプロセスのプロセスID(PID)が12483の場合、トレース・ファイルの名前は通常、dbs1_j000_12483.trcとなります。

    通知ハンドラに'chnf_callback'、登録IDに100を指定して登録が作成されているとします。ここで、'chnf_callback'はデータベースに定義されていないとします。この場合、JOBQトレース・ファイルには次の書式のメッセージが含まれます。

    ****************************************************************************
       Runtime error during execution of PL/SQL cbk chnf_callback for reg CHNF100.
       Error in PLSQL notification of msgid:
       Queue :
       Consumer Name :
       PLSQL function :chnf_callback
       Exception Occured, Error msg:
       ORA-00604: error occurred at recursive SQL level 2
       ORA-06550: line 1, column 7: 
       PLS-00201: identifier 'CHNF_CALLBACK' must be declared
       ORA-06550: line 1, column 7:
       PL/SQL: Statement ignored
    ****************************************************************************
    

    通知ハンドラの実行中に実行時エラーが発生した場合は、通知を受信していることを確認するために、簡略バージョンの通知ハンドラを作成してから、アプリケーション・ロジックを徐々に追加してください。

    非常に単純な通知ハンドラの例は次のとおりです。

    REM Create table in HR schema to hold count of notifications received.
    CREATE TABLE nfcount(cnt NUMBER);
    INSERT INTO nfcount (cnt) VALUES(0);
    COMMIT;
    CREATE OR REPLACE PROCEDURE chnf_callback
      (ntfnds IN CQ_NOTIFICATION$_DESCRIPTOR)
    IS
    BEGIN
      UPDATE nfcount SET cnt = cnt+1;
      COMMIT;
    END;
    /
    
  • トランザクションがコミットされてからエンド・ユーザーが通知を受信するまでに、タイム・ラグが発生しています。

登録の削除

登録を削除するには、登録IDをパラメータとして使用してプロシージャDBMS_CQ_NOTIFICATION.DEREGISTERをコールします。たとえば、次の文により、登録IDが21の登録が解除されます。

DBMS_CQ_NOTIFICATION.DEREGISTER(21);

登録を作成したユーザーまたはSYSユーザーのみが登録を解除できます。

CQNの構成: 使用例

この使用例では、開発者として従業員データ(名前、所在地、電話番号など)を提供するWebアプリケーションを管理しているとします。このアプリケーションはOracle Application Serverで実行され、使用量が多く、バックエンド・データベースにあるHR.EMPLOYEES表およびHR.DEPARTMENTS表の頻繁な問合せを処理します。この2つの表はあまり変更されないため、アプリケーションでは問合せ結果をキャッシュすることでパフォーマンスを改善できます。キャッシュにより、バックエンド・データベースへのラウンド・トリップ、およびサーバー側での実行待機時間が回避されます。

DBMS_CQ_NOTIFICATIONパッケージを使用すると、HR.EMPLOYEES表およびHR.DEPARTMENTS表に基づく問合せを登録できます。CQNを構成するステップは、次のとおりです。

  1. 「PL/SQL通知ハンドラの作成」の説明に従って、通知を処理するサーバー側PL/SQLストアド・プロシージャを作成します。
  2. 「問合せの登録」の説明に従って、HR.EMPLOYEES表およびHR.DEPARTMENTS表へのQRCNに対する問合せを登録します。

これらのステップの完了後は、ステップ2で登録した問合せの結果に対する変更がコミットされると、ステップ1で作成した通知ハンドラによってWebアプリケーションに変更が通知され、この直後に、バックエンド・データベースの問合せによりWebアプリケーションでキャッシュがリフレッシュされます。

PL/SQL通知ハンドラの作成

次の説明に従って、通知を処理するサーバー側ストアドPL/SQLプロシージャを作成します。

  1. AS SYSDBAでデータベースに接続します。
  2. 必要な権限をHRに付与します。
    GRANT EXECUTE ON DBMS_CQ_NOTIFICATION TO HR;
    GRANT CHANGE NOTIFICATION TO HR;
    
  3. 通知を受信するためにJOB_QUEUE_PROCESSESパラメータを有効化します。
    ALTER SYSTEM SET "JOB_QUEUE_PROCESSES"=4;
    
  4. SYS以外のユーザー(HRなど)としてデータベースに接続します。
  5. 受信した通知イベントのレコードを保持するデータベース表を作成します。
    -- Create table to record notification events.
    DROP TABLE nfevents;
    CREATE TABLE nfevents (
      regid      NUMBER,
      event_type NUMBER
    );
    
    -- Create table to record notification queries:
    DROP TABLE nfqueries;
    CREATE TABLE nfqueries (
      qid NUMBER,
      qop NUMBER
    );
    
    -- Create table to record changes to registered tables:
    DROP TABLE nftablechanges;
    CREATE TABLE nftablechanges (
      qid             NUMBER,
      table_name      VARCHAR2(100),
      table_operation NUMBER
    );
    
    -- Create table to record ROWIDs of changed rows:
    DROP TABLE nfrowchanges;
    CREATE TABLE nfrowchanges (
      qid        NUMBER,
      table_name VARCHAR2(100),
      row_id     VARCHAR2(2000)
    );
    
  6. 例18-6に示すように、プロシージャHR.chnf_callbackを作成します。

例18-6 サーバー側PL/SQL通知ハンドラの作成

CREATE OR REPLACE PROCEDURE chnf_callback (
  ntfnds IN CQ_NOTIFICATION$_DESCRIPTOR
)
IS
  regid           NUMBER;
  tbname          VARCHAR2(60);
  event_type      NUMBER;
  numtables       NUMBER;
  operation_type  NUMBER;
  numrows         NUMBER;
  row_id          VARCHAR2(2000);
  numqueries      NUMBER;
  qid             NUMBER;
  qop             NUMBER;

BEGIN
  regid := ntfnds.registration_id;
  event_type := ntfnds.event_type;

  INSERT INTO nfevents (regid, event_type)
  VALUES (chnf_callback.regid, chnf_callback.event_type);

  numqueries :=0;

  IF (event_type = DBMS_CQ_NOTIFICATION.EVENT_QUERYCHANGE) THEN
    numqueries := ntfnds.query_desc_array.count;

    FOR i IN 1..numqueries LOOP  -- loop over queries
      qid := ntfnds.query_desc_array(i).queryid;
      qop := ntfnds.query_desc_array(i).queryop;

      INSERT INTO nfqueries (qid, qop)
      VALUES(chnf_callback.qid, chnf_callback.qop);

      numtables := 0;
      numtables := ntfnds.query_desc_array(i).table_desc_array.count;

      FOR j IN 1..numtables LOOP  -- loop over tables
        tbname :=
          ntfnds.query_desc_array(i).table_desc_array(j).table_name;
        operation_type :=
          ntfnds.query_desc_array(i).table_desc_array(j).Opflags;

        INSERT INTO nftablechanges (qid, table_name, table_operation) 
        VALUES (
          chnf_callback.qid,
          tbname,
          operation_type
        );

        IF (bitand(operation_type, DBMS_CQ_NOTIFICATION.ALL_ROWS) = 0) THEN
          numrows := ntfnds.query_desc_array(i).table_desc_array(j).numrows;
        ELSE
          numrows :=0;  -- ROWID info not available
        END IF;

        -- Body of loop does not run when numrows is zero.
        FOR k IN 1..numrows LOOP  -- loop over rows
          Row_id :=
 ntfnds.query_desc_array(i).table_desc_array(j).row_desc_array(k).row_id;

          INSERT INTO nfrowchanges (qid, table_name, row_id)
          VALUES (chnf_callback.qid, tbname, chnf_callback.Row_id);

        END LOOP;  -- loop over rows
      END LOOP;  -- loop over tables
    END LOOP;  -- loop over queries
  END IF;
  COMMIT;
END;
/
問合せの登録

通知ハンドラの作成後に、例18-7に示すように、HR.chnf_callbackを通知ハンドラとして指定して、通知を受信する対象となる問合せを登録します。

例18-7 問合せの登録

DECLARE
  reginfo   CQ_NOTIFICATION$_REG_INFO;
  mgr_id    NUMBER;
  dept_id   NUMBER;
  v_cursor  SYS_REFCURSOR;
  regid     NUMBER;

BEGIN
  /* Register two queries for QRNC: */
  /* 1. Construct registration information.
        chnf_callback is name of notification handler.
        QOS_QUERY specifies result-set-change notifications. */

  reginfo := cq_notification$_reg_info (
    'chnf_callback',
    DBMS_CQ_NOTIFICATION.QOS_QUERY,
    0, 0, 0
  );

  /* 2. Create registration. */

  regid := DBMS_CQ_NOTIFICATION.new_reg_start(reginfo);

  OPEN v_cursor FOR
    SELECT dbms_cq_notification.CQ_NOTIFICATION_QUERYID, manager_id
    FROM HR.EMPLOYEES
    WHERE employee_id = 7902;
  CLOSE v_cursor;

  OPEN v_cursor FOR
    SELECT dbms_cq_notification.CQ_NOTIFICATION_QUERYID, department_id
    FROM HR.departments
    WHERE department_name = 'IT';
  CLOSE v_cursor;

  DBMS_CQ_NOTIFICATION.reg_end;
END;
/

新しく作成した登録を表示:

SELECT queryid, regid, TO_CHAR(querytext)
FROM user_cq_notification_queries;

結果は次のようになります。

QUERYID REGID                               TO_CHAR(QUERYTEXT)
------- ----- ------------------------------------------------
     22    41 SELECT HR.DEPARTMENTS.DEPARTMENT_ID
                FROM HR.DEPARTMENTS
                  WHERE HR.DEPARTMENTS.DEPARTMENT_NAME  = 'IT'

     21    41 SELECT HR.EMPLOYEES.MANAGER_ID
                FROM HR.EMPLOYEES
                  WHERE HR.EMPLOYEES.EMPLOYEE_ID  = 7902

次のトランザクションを実行すると、QUERYIDが22の問合せの結果が変更されます。

UPDATE DEPARTMENTS
SET DEPARTMENT_NAME = 'FINANCE'
WHERE department_name = 'IT';

通知プロシージャchnf_callback(例18-6で作成したプロシージャ)が実行されます。

通知イベントが記録される表を問合せ:

SELECT * FROM nfevents;

結果は次のようになります。

REGID EVENT_TYPE
----- ----------
   61          7

EVENT_TYPE 7は、EVENT_QUERYCHANGE(問合せ結果変更)に対応します。

登録済の表への変更が記録される表を問い合せます。

SELECT * FROM nftablechanges;

結果は次のようになります。

REGID     TABLE_NAME TABLE_OPERATION
----- -------------- ---------------
   42 HR.DEPARTMENTS               4

TABLE_OPERATION 4は、UPDATEOP(更新操作)に対応します。

変更された行のROWIDが記録される表を問い合せます。

SELECT * FROM nfrowchanges;

結果は次のようになります。

REGID     TABLE_NAME              ROWID
----- -------------- ------------------
   61 HR.DEPARTMENTS AAANkdAABAAALinAAF

OCIを使用したCQN登録の作成

この項では、OCIを使用したCQN登録の作成について説明します。OCIを使用する場合、通知ハンドラはクライアント側Cコールバック・プロシージャです。

トピック

問合せ結果セット通知でのOCI使用

連続問合せ(CQ)通知固有のQOS(サービス品質フラグ)を記録するには、サブスクリプション・ハンドルOCI_HTYPE_SUBSCRに属性OCI_ATTR_SUBSCR_CQ_QOSFLAGSを設定します。オブジェクトの細分化レベルではなく問合せの細分化レベルで登録するようにリクエストするには、属性OCI_ATTR_SUBSCR_CQ_QOSFLAGSOCI_SUBSCR_CQ_QOS_QUERYフラグ・ビットを設定します。

オプションで擬似列CQ_NOTIFICATION_QUERY_IDを指定して、登録済問合せの問合せIDを取得できます。この場合、細分化レベルは自動的には問合せレベルに変換されません。戻り時の擬似列の値は、問合せに割り当てられた一意の問合せIDに設定されます。OCIベースの登録の場合は問合せIDの擬似列を省略できますが、その場合は問合せIDが文ハンドルのREAD属性として送信されます。(この属性はOCI_ATTR_CQ_QUERYIDです)。

通知中に、クライアントが指定したコールバックが起動され、トップレベルの通知記述子が引数として渡されます。

変更された問合せの問合せIDに関する情報は、OCI_DTYPE_CQDESという特別な記述型を使用して送られます。問合せ記述子の集合(OCIColl)は、トップレベルの通知記述子内に埋め込まれます。各記述子はOCI_DTYPE_CQDES型です。この問合せ記述子には、次の属性が含まれています。

  • OCI_ATTR_CQDES_OPERATION: OCI_EVENT_QUERYCHANGEまたはOCI_EVENT_DEREGです。

  • OCI_ATTR_CQDES_QUERYID: 変更された問合せの問合せIDです。

  • OCI_ATTR_CQDES_TABLE_CHANGES: 問合せ結果セットが変更される原因となった表に対するDML操作を記述する表記述子の配列です。各表記述子はOCI_DTYPE_TABLE_CHDES型です。

関連項目:

OCI_DTYPE_CHDES

連続問合せ通知の登録のためのOCI使用

コール側セッションには、登録対象のすべてのオブジェクトに対するCHANGE NOTIFICATIONシステム権限およびSELECT権限が必要です。登録はデータベースに記録される永続エンティティであり、Oracle RACのすべてのインスタンスに表示されます。登録が問合せ精度の場合、Oracle RACインスタンスで問合せ結果セットが変更され、コミットされる原因となるトランザクションによって通知が生成されます。登録がオブジェクト精度の場合、Oracle RACインスタンスに登録済のオブジェクトを変更するトランザクションによって通知が生成されます。

マテリアライズド・ビューまたは非マテリアライズド・ビューが関係する問合せは、サポートされません

登録インタフェースは、コールバック、およびAQのネームスペース拡張(DBCHANGE)を使用して、問合せ対象オブジェクトの変更を通知します。

登録を作成するステップは、次のとおりです。

  1. OCI_EVENTSおよびOCI_OBJECTモードで環境を作成します。
  2. サブスクリプション・ハンドル属性OCI_ATTR_SUBSCR_NAMESPACEをネームスペースOCI_SUBSCR_NAMESPACE_DBCHANGEに設定します。
  3. サブスクリプション・ハンドル属性OCI_ATTR_SUBSCR_CALLBACKを、問合せハンドルに関連したOCIコールバックを格納するように設定します。このコールバックには、次のプロトタイプがあります。
    void notification_callback (void *ctx, OCISubscription *subscrhp, 
                                void *payload, ub4 paylen, void *desc, ub4 mode);
    

    パラメータの詳細は、『Oracle Call Interfaceプログラマーズ・ガイド』のOCIでの通知コールバックに関する項を参照してください。

  4. 必要に応じて、OCI_ATTR_SUBSCR_CTX属性を使用してクライアント固有のコンテキストを関連付けます。
  5. OCI_ATTR_SUBSCR_TIMEOUT属性を設定して、ub4タイムアウト間隔を秒単位で指定します。設定しない場合、タイムアウトはありません。
  6. OCI_ATTR_SUBSCR_QOSFLAGS属性に次の値を使用して、QOS (サービス品質)レベルを設定します。
    • OCI_SUBSCR_QOS_PURGE_ON_NTFNフラグでは、最初の通知で登録がパージされます。

    • OCI_SUBSCR_QOS_RELIABLEフラグでは、通知が持続されます。Oracle RACの残存インスタンスを使用すると、ノードが失敗した後でも連続問合せ通知メッセージを送信および取得できます。これは、この登録に関連する無効化が、データベースに永続的にキューされるためです。FALSEである場合、無効化されたメッセージは高速インメモリー・キューにエンキューされます。このオプションは、登録の永続性ではなく、通知の永続性を記述します。デフォルトでは、登録は自動的に持続されます。

  7. OCISubscriptionRegister()をコールし、DBCHANGEネームスペースに新しい登録を作成します。
  8. 複数の問合せ文をサブスクリプション・ハンドルに関連付けるには、文ハンドルOCI_HTYPE_STMTOCI_ATTR_CHNF_REGHANDLE属性を設定します。問合せが実行されると、登録が完了します。

    関連項目:

    OCI_ATTR_CHNF_REGHANDLEの詳細は、『Oracle Call Interfaceプログラマーズ・ガイド』を参照してください

  9. 必要に応じて、サブスクリプションの登録を解除します。クライアントはパラメータとしてサブスクリプション・ハンドルを使用して、OCISubscriptionRegister()ファンクションをコールできます。

サブスクリプション・ハンドルへの文ハンドルのバインドが有効なのは、問合せの初回の実行時のみです。アプリケーションで後続の実行時に同じOCI statement文ハンドルを使用する必要がある場合は、文ハンドルの登録ハンドル属性に値を再移入する必要があります。文ハンドルをサブスクリプション・ハンドルにバインドできるのは、文が問合せである場合のみです(実行時に判別されます)。実行の一部としてDML文が実行されると、例外が発行されます。

連続問合せ通知のOCIサブスクリプション・ハンドル属性の使用

連続問合せ通知のサブスクリプション・ハンドル属性は、汎用属性(すべてのサブスクリプションに対して共通)と、連続問合せ通知に固有のネームスペース特有属性に分けられます。

文ハンドルのWRITE属性を変更できるのは、登録の作成前のみです。

汎用属性: すべてのサブスクリプションに共通

OCI_ATTR_SUBSCR_NAMESPACE(WRITE): サブスクリプション・ハンドルの場合、OCI_SUBSCR_NAMESPACE_DBCHANGEに、この属性を設定します。

OCI_ATTR_SUBSCR_CALLBACK(WRITE): サブスクリプション・ハンドルに関連するコールバックを格納するために、この属性を使用します。通知を受信すると、コールバックが実行されます。

新しい連続問合せ通知メッセージが作成されると、無効化に関する詳細情報が含まれるOCI_DTYPE_CHDES型の記述子がdescに設定されてコールバックがリスナー・スレッド内で呼び出されます。

OCI_ATTR_SUBSCR_QOSFLAGS: この属性は次の値を含む汎用フラグです。

#define OCI_SUBSCR_QOS_RELIABLE             0x01                 /* reliable */
#define OCI_SUBSCR_QOS_PURGE_ON_NTFN        0x10      /* purge on first ntfn */
  • OCI_SUBSCR_QOS_RELIABLE: このビットを設定し、通知を持続させます。このため、Oracle RACクラスタの残存インスタンスを使用して、ノードの失敗後も無効化メッセージを送信および取得できます。これは、この登録IDに関連する無効化が、データベースに永続的にキューされるためです。このビットがFALSEの場合、無効化は高速インメモリー・キューにエンキューされます。このオプションは、登録の永続性ではなく、通知の永続性を記述します。デフォルトでは、登録は自動的に持続されます。

  • OCI_SUBSCR_QOS_PURGE_ON_NTFN: このビットを設定し、最初の通知で登録をパージできるようにします。

パラレルのサンプルは、『Oracle Call Interfaceプログラマーズ・ガイド』のOCIのパブリッシュ・サブスクライブ登録機能に関する項を参照してください。

OCI_ATTR_SUBSCR_CQ_QOSFLAGS: この属性では、連続問合せ通知固有の次のQOSフラグを記述します(モードはWRITE、データ型はub4)。

  • 0x1 OCI_SUBSCR_CQ_QOS_QUERY: このフラグを設定し、問合せレベルの細分性が必須であることを示します。通知は、問合せ結果セットが変更される場合にのみ生成します。デフォルトでは、このQOSレベルにFalseの正数はありません。

  • 0x2 OCI_SUBSCR_CQ_QOS_BEST_EFFORT: このフラグを設定し、ベスト・エフォート・フィルタリングが許容されることを示します。アプリケーションをキャッシュすることで使用できます。データベースでは、評価のコストに基づく経験則を使用し、完全プルーニングを回避できる場合があります。

OCI_ATTR_SUBSCR_TIMEOUT: この属性を使用して、秒単位で定義したub4タイムアウト値を指定します。タイムアウト値が0(ゼロ)または未指定の場合、登録は明示的に解除されるまで有効です。

ネームスペース固有または機能固有の属性

次の属性は、ネームスペース固有または連続問合せ通知機能固有のものです。

OCI_ATTR_CHNF_TABLENAMES(データ型は(OCIColl *)): これらは、登録された表名のリストを取得するための属性です。これらの属性は、問合せの実行後にサブスクリプション・ハンドルから使用できます。

OCI_ATTR_CHNF_ROWIDS: ブール型属性(デフォルトはFALSE)です。TRUEの場合、連続問合せ通知メッセージには、操作タイプやROWIDなどの行レベルの詳細が含まれます。

OCI_ATTR_CHNF_OPERATIONS: ub4フラグを使用して、操作タイプに基づいて通知をフィルタで選択します。登録が問合せレベルの細分性を持つ場合、このオプションは無視されます。用意されているフラグは、次のとおりです。

  • OCI_OPCODE_ALL - すべての操作

  • OCI_OPCODE_INSERT- 表に対する挿入操作

  • OCI_OPCODE_UPDATE- 表に対する更新操作

  • OCI_OPCODE_DELETE - 表に対する削除操作

OCI_ATTR_CHNF_CHANGELAG: クライアントはこのub4値を使用して、遅延させるトランザクション数を指定できます。クライアントは、このオプションを連続問合せ通知メッセージのスロットル・メカニズムとしても使用できます。このオプションを選択すると、OCI_ATTR_CHNF_ROWIDSTRUEであっても、通知にROWIDレベルの細分性を持つ情報が含まれなくなります。登録が問合せレベルの細分性を持つ場合、このオプションは無視されます。

OCISubscriptionRegister()のコールが起動されると、先行する属性(汎用、名前固有、または機能固有)はすべて、作成済の登録では変更不可になります。これらの属性への変更は、すでに作成されている登録には反映されませんが、新しく作成された、同じ登録ハンドルを使用する登録には反映されます。

関連項目:

連続問合せ通知の記述子の属性の詳細は、『Oracle Call Interfaceプログラマーズ・ガイド』を参照してください

NTFNグループ化オプションを使用して、通知の間隔を置くことができます。関連する汎用通知属性は次のとおりです。

OCI_ATTR_SUBSCR_NTFN_GROUPING_VALUE
OCI_ATTR_SUBSCR_NTFN_GROUPING_TYPE
OCI_ATTR_SUBSCR_NTFN_GROUPING_START_TIME
OCI_ATTR_SUBSCR_NTFN_GROUPING_REPEAT_COUNT

関連項目:

データベースへの直接パブリッシュ・サブスクライブ登録のこれらの属性の詳細は、『Oracle Call Interfaceプログラマーズ・ガイド』を参照してください

OCI_ATTR_CQ_QUERYID属性

OCIStmtExecute()のコールによる登録後、文ハンドルOCI_HTYPE_STMTOCI_ATTR_CQ_QUERYID属性が登録済問合せの問合せIDを取得します。

関連項目:

OCI_ATTR_CQ_QUERYIDの詳細は、『Oracle Call Interfaceプログラマーズ・ガイド』を参照してください

OCI連続問合せ通知の記述子の使用

連続問合せ通知の記述子は、アプリケーションによって指定された通知コールバックのdescパラメータに渡されます。次の属性は、連続問合せ通知に固有のものです。連続問合せ通知の記述子のOCI型定数は、OCI_DTYPE_CHDESです。

通知コールバックは、トップレベルの通知記述子OCI_DTYPE_CHDESを引数として受け取ります。この記述子には、イベント・タイプがOCI_EVENT_QUERYCHANGEであるかOCI_EVENT_OBJCHANGEであるかに基づいて、OCI_DTYPE_CQDESまたはOCI_DTYPE_TABLE_CHDES記述子のコレクションが含まれます。OCI_EVENT_QUERYCHANGE型の通知の場合、連続問合せ記述子には表連続問合せ記述子の配列が埋め込まれます。ROWIDレベルの細分性を持つ情報がリクエストされた場合、各OCI_DTYPE_TABLE_CHDESには変更された各ROWIDに対応する行レベルの連続問合せ記述子(OCI_DTYPE_ROW_CHDES)の配列が含まれます。

OCI_DTYPE_CHDES

これはトップレベルの連続問合せ通知記述子型です。

OCI_ATTR_CHDES_DBNAME (oratext *): データベースの名前(連続問合せ通知のソース)

OCI_ATTR_CHDES_XID (RAW(8)): メッセージのメッセージID

OCI_ATTR_CHDES_NFYTYPE - 通知タイプを示すフラグ。

  • 0x0 OCI_EVENT_NONE: 連続問合せ通知に関する追加情報なし

  • 0x1 OCI_EVENT_STARTUP: インスタンスの起動

  • 0x2 OCI_EVENT_SHUTDOWN: インスタンスの停止

  • 0x3 OCI_EVENT_SHUTDOWN_ANY: 任意のインスタンスの停止: Oracle Real Application Clusters (RAC)

  • 0x5 OCI_EVENT_DEREG: 登録解除またはタイムアウト

  • 0x6 OCI_EVENT_OBJCHANGE: オブジェクト変更通知

  • 0x7 OCI_EVENT_QUERYCHANGE: 問合せ変更通知

OCI_ATTR_CHDES_TABLE_CHANGES: 表またはデータ型(OCIColl *)の操作を記述するコレクション型。この属性は、OCI_ATTR_CHDES_NFTYPE属性がOCI_EVENT_OBJCHANGE型の場合のみ表示され、そうでない場合はNULLになります。コレクションの各要素は、OCI_DTYPE_TABLE_CHDES型の連続問合せ記述子の表です。

OCI_ATTR_CHDES_QUERIES: 無効化された問合せを記述するコレクション型。コレクションの各メンバーはOCI_DTYPE_CQDES型です。この属性は、OCI_ATTR_CHDES_NFTYPE属性がOCI_EVENT_QUERYCHANGE型の場合のみ表示され、そうでない場合はNULLになります。

OCI_DTYPE_CQDES

この通知記述子は、通常はDMLトランザクションまたはDDLトランザクションのコミットに応じて、無効化された問合せを記述します。次の属性が含まれます。

  • OCI_ATTR_CQDES_OPERATION (ub4, READ): 問合せで発生した操作。これは次の値のいずれかです。

    • OCI_EVENT_QUERYCHANGE: 問合せ結果セットの変更

    • OCI_EVENT_DEREG: 問合せの登録解除

  • OCI_ATTR_CQDES_TABLE_CHANGES (OCIColl *, READ): 問合せ結果セットの変更原因となった表に対するDMLまたはDDL操作を記述する、表連続問合せ記述子のコレクション。コレクションの各要素はOCI_DTYPE_TABLE_CHDES型です。

  • OCI_ATTR_CQDES_QUERYID (ub8, READ): 無効化された問合せの問合せID。

OCI_DTYPE_TABLE_CHDES

この通知記述子は、登録済問合せに関係する表の変更内容に関する情報を送信します。次の属性が含まれます。

  • OCI_ATTR_CHDES_TABLE_NAME (oratext *): スキーマ注釈付き表名。

  • OCI_ATTR_CHDES_TABLE_OPFLAGS (ub4)- 表への操作を示すフラグのフィールド。次の各フラグ・フィールドは、属性内の個別のビット位置にあります。

    • 0x1 OCI_OPCODE_ALLROWS: 表は完全に無効化されています。

    • 0x2 OCI_OPCODE_INSERT: 表に対する挿入操作。

    • 0x4 OCI_OPCODE_UPDATE: 表に対する更新操作。

    • 0x8 OCI_OPCODE_DELETE: 表に対する削除操作。

    • 0x10 OCI_OPCODE_ALTER: 表は変更されました(スキーマ変更)。これには、行の移行を引き起こしたDDL文と内部操作が含まれます。

    • 0x20 OCI_OPCODE_DROP: 表は削除されました。

  • OCI_ATTR_CHDES_TABLE_ROW_CHANGES - これは、表内の行の変更を示す埋込みコレクションです。コレクションの各要素は、次の属性を持つOCI_DTYPE_ROW_CHDES型の行連続問合せ記述子です。

    • OCI_ATTR_CHDES_ROW_ROWID (OraText *)- ROWIDの文字列表現。

    • OCI_ATTR_CHDES_ROW_OPFLAGS - 操作タイプ(INSERTUPDATEDELETEまたはOTHER)を反映します。

関連項目:

連続問合せ通知の記述子の属性の詳細は、『Oracle Call Interfaceプログラマーズ・ガイド』を参照してください

OCIサンプル・プログラムでの連続問合せ通知のデモンストレーション

例18-8は単純なOCIプログラム、demoquery.cです。リストにあるコメントを参照してください。コール側セッションには、登録対象のすべてのオブジェクトに対するCHANGE NOTIFICATIONシステム権限およびSELECT権限が必要です。

例18-8 連続問合せ通知を示すプログラム・リスト

/* Copyright (c) 2010, Oracle. All rights reserved.  */
 
#ifndef S_ORACLE
# include <oratypes.h>
#endif
 
/**************************************************************************
 *This is a DEMO program. To test, compile the file to generate the executable
 *demoquery. Then demoquery can be invoked from a command prompt.
 *It will have the following output:
 
Initializing OCI Process
Registering query : select last_name, employees.department_id, department_name 
                     from employees, departments 
                     where employee_id = 200 
                     and employees.department_id = departments.department_id
Query Id 23
Waiting for Notifications

*Then from another session, log in as HR/<password> and perform the following
* DML transactions. It will cause two notifications to be generated.
 
update departments set department_name ='Global Admin' where department_id=10;
commit;
update departments set department_name ='Administration' where department_id=10;
commit;

*The demoquery program will now show the following output corresponding
*to the notifications received.
 
 
Query 23 is changed
Table changed is HR.DEPARTMENTS table_op 4
Row changed is AAAMBoAABAAAKX2AAA row_op 4
Query 23 is changed
Table changed is HR.DEPARTMENTS table_op 4
Row changed is AAAMBoAABAAAKX2AAA row_op 4
 
 
*The demo program waits for exactly 10 notifications to be received before
*logging off and unregistering the subscription.
 
***************************************************************************/
 
/*---------------------------------------------------------------------------
                     PRIVATE TYPES AND CONSTANTS
  ---------------------------------------------------------------------------*/

/*---------------------------------------------------------------------------
                     STATIC FUNCTION DECLARATIONS 
  ---------------------------------------------------------------------------*/
 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <oci.h>
 
#define MAXSTRLENGTH 1024
#define bit(a,b) ((a)&(b))
 
static int notifications_processed = 0;
static OCISubscription *subhandle1 = (OCISubscription *)0;
static OCISubscription *subhandle2 = (OCISubscription *)0;
static void checker(/*_ OCIError *errhp, sword status _*/);
static void registerQuery(/*_ OCISvcCtx *svchp, OCIError *errhp, OCIStmt *stmthp,
                           OCIEnv *envhp _*/);
static void myCallback (/*_  dvoid *ctx, OCISubscription *subscrhp, 
                        dvoid *payload, ub4 *payl, dvoid *descriptor, 
                        ub4 mode _*/);
static int NotificationDriver(/*_ int argc, char *argv[]  _*/);
static sword status;
static boolean logged_on = FALSE;
static void processRowChanges(OCIEnv *envhp, OCIError *errhp, OCIStmt *stmthp, 
                               OCIColl *row_changes);
static void processTableChanges(OCIEnv *envhp, OCIError *errhp,
                 OCIStmt *stmthp, OCIColl *table_changes);
static void processQueryChanges(OCIEnv *envhp, OCIError *errhp, OCIStmt *stmthp,
                 OCIColl *query_changes);
static int nonractests2(/*_ int argc, char *argv[] _*/);
 
 
int main(int argc, char **argv)
{
 
  NotificationDriver(argc, argv);
  return 0;
}
 
 
int NotificationDriver(argc, argv)
int argc;
char *argv[];
{
  OCIEnv *envhp;
  OCISvcCtx *svchp, *svchp2;
  OCIError *errhp, *errhp2;
  OCISession *authp, *authp2;
  OCIStmt *stmthp, *stmthp2;
  OCIDuration dur, dur2;
  int i;
  dvoid *tmp;
  OCISession *usrhp;
  OCIServer *srvhp;
 
  printf("Initializing OCI Process\n");
/* Initialize the environment. The environment must be initialized
     with OCI_EVENTS and OCI_OBJECT to create a continuous query notification
     registration and receive notifications.
  */
  OCIEnvCreate( (OCIEnv **) &envhp, OCI_EVENTS|OCI_OBJECT, (dvoid *)0,
                    (dvoid * (*)(dvoid *, size_t)) 0,
                    (dvoid * (*)(dvoid *, dvoid *, size_t))0,
                    (void (*)(dvoid *, dvoid *)) 0,
                    (size_t) 0, (dvoid **) 0 );
 
  OCIHandleAlloc( (dvoid *) envhp, (dvoid **) &errhp, OCI_HTYPE_ERROR,
                         (size_t) 0, (dvoid **) 0);
   /* server contexts */
  OCIHandleAlloc((dvoid *) envhp, (dvoid **) &srvhp, OCI_HTYPE_SERVER, 
                 (size_t) 0, (dvoid **) 0);
  OCIHandleAlloc((dvoid *) envhp, (dvoid **) &svchp, OCI_HTYPE_SVCCTX,
                 (size_t) 0, (dvoid **) 0);
   checker(errhp,OCIServerAttach(srvhp, errhp, (text *) 0, (sb4) 0, 
                                 (ub4) OCI_DEFAULT));
  /* set attribute server context in the service context */
  OCIAttrSet( (dvoid *) svchp, (ub4) OCI_HTYPE_SVCCTX, (dvoid *)srvhp,
              (ub4) 0, (ub4) OCI_ATTR_SERVER, (OCIError *) errhp);
 
   /* allocate a user context handle */
  OCIHandleAlloc((dvoid *)envhp, (dvoid **)&usrhp, (ub4) OCI_HTYPE_SESSION,
                               (size_t) 0, (dvoid **) 0);
 
  OCIAttrSet((dvoid *)usrhp, (ub4)OCI_HTYPE_SESSION,
             (dvoid *)((text *)"HR"), (ub4)strlen((char *)"HR"), 
              OCI_ATTR_USERNAME, errhp);
 
 OCIAttrSet((dvoid *)usrhp, (ub4)OCI_HTYPE_SESSION,
            (dvoid *)((text *)"HR"), (ub4)strlen((char *)"HR"),  
             OCI_ATTR_PASSWORD, errhp);
   checker(errhp,OCISessionBegin (svchp, errhp, usrhp, OCI_CRED_RDBMS,
           OCI_DEFAULT));
   /* Allocate a statement handle */
  OCIHandleAlloc( (dvoid *) envhp, (dvoid **) &stmthp,
                                (ub4) OCI_HTYPE_STMT, 52, (dvoid **) &tmp);
 
  OCIAttrSet((dvoid *)svchp, (ub4)OCI_HTYPE_SVCCTX, (dvoid *)usrhp, (ub4)0,
                       OCI_ATTR_SESSION, errhp);
 
  registerQuery(svchp, errhp, stmthp, envhp);
  printf("Waiting for Notifications\n");
  while (notifications_processed !=10)
  {
    sleep(1);
  }
  printf ("Going to unregister HR\n");
  fflush(stdout);
  /* Unregister HR */
  checker(errhp,
           OCISubscriptionUnRegister(svchp, subhandle1, errhp, OCI_DEFAULT));
  checker(errhp, OCISessionEnd(svchp, errhp, usrhp, (ub4) 0));
   printf("HR Logged off.\n");
 
  if (subhandle1)
     OCIHandleFree((dvoid *)subhandle1, OCI_HTYPE_SUBSCRIPTION);
  if (stmthp)
     OCIHandleFree((dvoid *)stmthp, OCI_HTYPE_STMT);
  if (srvhp)
     OCIHandleFree((dvoid *) srvhp, (ub4) OCI_HTYPE_SERVER);
  if (svchp)
     OCIHandleFree((dvoid *) svchp, (ub4) OCI_HTYPE_SVCCTX);
  if (authp)
     OCIHandleFree((dvoid *) usrhp, (ub4) OCI_HTYPE_SESSION);
  if (errhp)
     OCIHandleFree((dvoid *) errhp, (ub4) OCI_HTYPE_ERROR);
  if (envhp)
     OCIHandleFree((dvoid *) envhp, (ub4) OCI_HTYPE_ENV);
 
 
  return 0;
 
}
 
void checker(errhp, status)
OCIError *errhp;
sword status;
{
  text errbuf[512];
  sb4 errcode = 0;
  int retval = 1;
 
  switch (status)
  {
  case OCI_SUCCESS:
    retval = 0;
    break;
  case OCI_SUCCESS_WITH_INFO:
    (void) printf("Error - OCI_SUCCESS_WITH_INFO\n");
    break;
 case OCI_NEED_DATA:
    (void) printf("Error - OCI_NEED_DATA\n");
    break;
  case OCI_NO_DATA:
    (void) printf("Error - OCI_NODATA\n");
    break;
  case OCI_ERROR:
    (void) OCIErrorGet((dvoid *)errhp, (ub4) 1, (text *) NULL, &errcode,
                        errbuf, (ub4) sizeof(errbuf), OCI_HTYPE_ERROR);
    (void) printf("Error - %.*s\n", 512, errbuf);
    break;
  case OCI_INVALID_HANDLE:
    (void) printf("Error - OCI_INVALID_HANDLE\n");
    break;
  case OCI_STILL_EXECUTING:
    (void) printf("Error - OCI_STILL_EXECUTE\n");
    break; 
  case OCI_CONTINUE:
    (void) printf("Error - OCI_CONTINUE\n");
    break; 
  default:
    break;
 }
  if (retval)
  {
    exit(1);
  }
}
 
 
void processRowChanges(OCIEnv *envhp, OCIError *errhp, OCIStmt *stmthp,
                         OCIColl *row_changes)
{
  dvoid **row_descp;
  dvoid *row_desc;
  boolean exist; 
  ub2 i, j;
  dvoid *elemind = (dvoid *)0;
  oratext *row_id;
  ub4 row_op;
 
 
   sb4 num_rows;
   if (!row_changes) return;
    checker(errhp, OCICollSize(envhp, errhp,
                    (CONST OCIColl *) row_changes, &num_rows));
    for (i=0; i<num_rows; i++)
    {
      checker(errhp, OCICollGetElem(envhp,
                     errhp, (OCIColl *) row_changes,
                     i, &exist, &row_descp, &elemind));
 
      row_desc = *row_descp;
      checker(errhp, OCIAttrGet (row_desc, 
                  OCI_DTYPE_ROW_CHDES, (dvoid *)&row_id,
                  NULL, OCI_ATTR_CHDES_ROW_ROWID, errhp));
      checker(errhp, OCIAttrGet (row_desc, 
                  OCI_DTYPE_ROW_CHDES, (dvoid *)&row_op, 
                  NULL, OCI_ATTR_CHDES_ROW_OPFLAGS, errhp));
 
      printf ("Row changed is %s row_op %d\n", row_id, row_op);
      fflush(stdout); 
    }  
}
 
void processTableChanges(OCIEnv *envhp, OCIError *errhp, OCIStmt *stmthp,
                         OCIColl *table_changes)
{
  dvoid **table_descp;
  dvoid *table_desc;
  dvoid **row_descp;
  dvoid *row_desc;
  OCIColl *row_changes = (OCIColl *)0;
  boolean exist; 
  ub2 i, j;
  dvoid *elemind = (dvoid *)0;
  oratext *table_name;
  ub4 table_op;
 
 
   sb4 num_tables;
   if (!table_changes) return;
    checker(errhp, OCICollSize(envhp, errhp,
                    (CONST OCIColl *) table_changes, &num_tables));
    for (i=0; i<num_tables; i++)
    {
      checker(errhp, OCICollGetElem(envhp,
                     errhp, (OCIColl *) table_changes,
                     i, &exist, &table_descp, &elemind));
 
      table_desc = *table_descp;
      checker(errhp, OCIAttrGet (table_desc, 
                  OCI_DTYPE_TABLE_CHDES, (dvoid *)&table_name,
                  NULL, OCI_ATTR_CHDES_TABLE_NAME, errhp));
      checker(errhp, OCIAttrGet (table_desc, 
                  OCI_DTYPE_TABLE_CHDES, (dvoid *)&table_op, 
                  NULL, OCI_ATTR_CHDES_TABLE_OPFLAGS, errhp));
      checker(errhp, OCIAttrGet (table_desc, 
                  OCI_DTYPE_TABLE_CHDES, (dvoid *)&row_changes, 
                  NULL, OCI_ATTR_CHDES_TABLE_ROW_CHANGES, errhp));
 
      printf ("Table changed is %s table_op %d\n", table_name,table_op);
      fflush(stdout); 
     if (!bit(table_op, OCI_OPCODE_ALLROWS))
       processRowChanges(envhp, errhp, stmthp, row_changes);
    }  
}
 
void processQueryChanges(OCIEnv *envhp, OCIError *errhp, OCIStmt *stmthp,
                         OCIColl *query_changes)
{
  sb4 num_queries;
  ub8 queryid;
  OCINumber qidnum;
  ub4 queryop;
  dvoid *elemind = (dvoid *)0;
  dvoid *query_desc;
  dvoid **query_descp;
  ub2 i;
  boolean exist;
  OCIColl *table_changes = (OCIColl *)0;
  
  if (!query_changes) return;
  checker(errhp, OCICollSize(envhp, errhp,
                     (CONST OCIColl *) query_changes, &num_queries));
  for (i=0; i < num_queries; i++)
  {
    checker(errhp, OCICollGetElem(envhp,
                     errhp, (OCIColl *) query_changes,
                     i, &exist, &query_descp, &elemind));
 
    query_desc = *query_descp;
    checker(errhp, OCIAttrGet (query_desc,
                  OCI_DTYPE_CQDES, (dvoid *)&queryid,
                  NULL, OCI_ATTR_CQDES_QUERYID, errhp));
    checker(errhp, OCIAttrGet (query_desc,
                  OCI_DTYPE_CQDES, (dvoid *)&queryop,
                  NULL, OCI_ATTR_CQDES_OPERATION, errhp));
    printf(" Query %d is changed\n", queryid);
    if (queryop == OCI_EVENT_DEREG)
      printf("Query Deregistered\n");
      checker(errhp, OCIAttrGet (query_desc,
                  OCI_DTYPE_CQDES, (dvoid *)&table_changes,
                  NULL, OCI_ATTR_CQDES_TABLE_CHANGES, errhp));
      processTableChanges(envhp, errhp, stmthp, table_changes);
 
 
   }
}
 
   
void myCallback (ctx, subscrhp, payload, payl, descriptor, mode)
dvoid *ctx;
OCISubscription *subscrhp;
dvoid *payload;
ub4 *payl; 
dvoid *descriptor;
ub4 mode;
{
  OCIColl *table_changes = (OCIColl *)0;
  OCIColl *row_changes = (OCIColl *)0;
  dvoid *change_descriptor = descriptor;
  ub4 notify_type;
  ub2 i, j;
  OCIEnv *envhp;
  OCIError *errhp;
  OCIColl *query_changes = (OCIColl *)0;
  OCIServer *srvhp;
  OCISvcCtx *svchp;
  OCISession *usrhp;
  dvoid     *tmp; 
  OCIStmt *stmthp;
 
 (void)OCIEnvInit( (OCIEnv **) &envhp, OCI_DEFAULT, (size_t)0, (dvoid **)0 );
  
  (void) OCIHandleAlloc( (dvoid *) envhp, (dvoid **) &errhp, OCI_HTYPE_ERROR,
                   (size_t) 0, (dvoid **) 0);
   /* server contexts */
  (void) OCIHandleAlloc( (dvoid *) envhp, (dvoid **) &srvhp, OCI_HTYPE_SERVER,
                   (size_t) 0, (dvoid **) 0);
 
  (void) OCIHandleAlloc( (dvoid *) envhp, (dvoid **) &svchp, OCI_HTYPE_SVCCTX,
                   (size_t) 0, (dvoid **) 0);
 
  OCIAttrGet (change_descriptor, OCI_DTYPE_CHDES, (dvoid *) &notify_type,
              NULL, OCI_ATTR_CHDES_NFYTYPE, errhp);
  fflush(stdout);
  if (notify_type == OCI_EVENT_SHUTDOWN ||
      notify_type == OCI_EVENT_SHUTDOWN_ANY)
  {
     printf("SHUTDOWN NOTIFICATION RECEIVED\n");
     fflush(stdout);
     notifications_processed++;
     return;
  }
 if (notify_type == OCI_EVENT_STARTUP)
  {
     printf("STARTUP NOTIFICATION RECEIVED\n");
     fflush(stdout);
     notifications_processed++; 
     return;
  }
  
  notifications_processed++;
  checker(errhp, OCIServerAttach( srvhp, errhp, (text *) 0, (sb4) 0,
                                  (ub4) OCI_DEFAULT));
 
  OCIHandleAlloc( (dvoid *) envhp, (dvoid **) &svchp, (ub4) OCI_HTYPE_SVCCTX,
                  52, (dvoid **) &tmp);
  /* set attribute server context in the service context */
  OCIAttrSet( (dvoid *) svchp, (ub4) OCI_HTYPE_SVCCTX, (dvoid *)srvhp,
              (ub4) 0, (ub4) OCI_ATTR_SERVER, (OCIError *) errhp);
 
  /* allocate a user context handle */
  OCIHandleAlloc((dvoid *)envhp, (dvoid **)&usrhp, (ub4) OCI_HTYPE_SESSION,
           (size_t) 0, (dvoid **) 0);
 
  OCIAttrSet((dvoid *)usrhp, (ub4)OCI_HTYPE_SESSION,
           (dvoid *)"HR", (ub4)strlen("HR"), OCI_ATTR_USERNAME, errhp);
 
  OCIAttrSet((dvoid *)usrhp, (ub4)OCI_HTYPE_SESSION,
           (dvoid *)"HR", (ub4)strlen("HR"),
           OCI_ATTR_PASSWORD, errhp);
 
  checker(errhp, OCISessionBegin (svchp, errhp, usrhp, OCI_CRED_RDBMS,
                                   OCI_DEFAULT));
 
  OCIAttrSet((dvoid *)svchp, (ub4)OCI_HTYPE_SVCCTX,
           (dvoid *)usrhp, (ub4)0, OCI_ATTR_SESSION, errhp);
 
  /* Allocate a statement handle */
  OCIHandleAlloc( (dvoid *) envhp, (dvoid **) &stmthp,
                  (ub4) OCI_HTYPE_STMT, 52, (dvoid **) &tmp);
 
  if (notify_type == OCI_EVENT_OBJCHANGE)
  {
    checker(errhp, OCIAttrGet (change_descriptor,
                OCI_DTYPE_CHDES, &table_changes, NULL,
                OCI_ATTR_CHDES_TABLE_CHANGES, errhp));
    processTableChanges(envhp, errhp, stmthp, table_changes);
  }
  else if (notify_type == OCI_EVENT_QUERYCHANGE)
  {
     checker(errhp, OCIAttrGet (change_descriptor,
                OCI_DTYPE_CHDES, &query_changes, NULL,
                OCI_ATTR_CHDES_QUERIES, errhp));
      processQueryChanges(envhp, errhp, stmthp, query_changes);
  }
   checker(errhp, OCISessionEnd(svchp, errhp, usrhp, OCI_DEFAULT));
  checker(errhp, OCIServerDetach(srvhp, errhp, OCI_DEFAULT));
 if (stmthp)
    OCIHandleFree((dvoid *)stmthp, OCI_HTYPE_STMT);
  if (errhp)
    OCIHandleFree((dvoid *)errhp, OCI_HTYPE_ERROR);
  if (srvhp)
    OCIHandleFree((dvoid *)srvhp, OCI_HTYPE_SERVER);
  if (svchp)
    OCIHandleFree((dvoid *)svchp, OCI_HTYPE_SVCCTX);
  if (usrhp)
    OCIHandleFree((dvoid *)usrhp, OCI_HTYPE_SESSION);
  if (envhp)
    OCIHandleFree((dvoid *)envhp, OCI_HTYPE_ENV);
 
}
 
void registerQuery(svchp, errhp, stmthp, envhp)
OCISvcCtx *svchp; 
OCIError *errhp; 
OCIStmt *stmthp;
OCIEnv *envhp;
{
  OCISubscription *subscrhp;
  ub4 namespace = OCI_SUBSCR_NAMESPACE_DBCHANGE;
  ub4 timeout = 60;
  OCIDefine *defnp1 = (OCIDefine *)0;
  OCIDefine *defnp2 = (OCIDefine *)0;
  OCIDefine *defnp3 = (OCIDefine *)0;
  OCIDefine *defnp4 = (OCIDefine *)0;
  OCIDefine *defnp5 = (OCIDefine *)0;
  int mgr_id =0;
text query_text1[] = "select last_name, employees.department_id, department_name \
 from employees,departments where employee_id = 200 and employees.department_id =\
  departments.department_id";
 
  ub4 num_prefetch_rows = 0;
  ub4 num_reg_tables;
  OCIColl *table_names;
  ub2 i;
  boolean rowids = TRUE;
  ub4 qosflags = OCI_SUBSCR_CQ_QOS_QUERY  ;
  int empno=0;
  OCINumber qidnum;
  ub8 qid;
  char outstr[MAXSTRLENGTH], dname[MAXSTRLENGTH];
  int q3out;
 
    fflush(stdout);
  /* allocate subscription handle */
  OCIHandleAlloc ((dvoid *) envhp, (dvoid **) &subscrhp,
                  OCI_HTYPE_SUBSCRIPTION, (size_t) 0, (dvoid **) 0);
  
  /* set the namespace to DBCHANGE */
  checker(errhp, OCIAttrSet (subscrhp, OCI_HTYPE_SUBSCRIPTION,
                  (dvoid *) &namespace, sizeof(ub4),
                  OCI_ATTR_SUBSCR_NAMESPACE, errhp));
  
  /* Associate a notification callback with the subscription */
  checker(errhp, OCIAttrSet (subscrhp, OCI_HTYPE_SUBSCRIPTION,
                  (void *)myCallback, 0, OCI_ATTR_SUBSCR_CALLBACK, errhp));
 /* Allow extraction of rowid information */
  checker(errhp, OCIAttrSet (subscrhp, OCI_HTYPE_SUBSCRIPTION,
                  (dvoid *)&rowids, sizeof(ub4), 
                  OCI_ATTR_CHNF_ROWIDS, errhp));
   
     checker(errhp, OCIAttrSet (subscrhp, OCI_HTYPE_SUBSCRIPTION,
                  (dvoid *)&qosflags, sizeof(ub4),
                  OCI_ATTR_SUBSCR_CQ_QOSFLAGS, errhp));
 
  /* Create a new registration in the DBCHANGE namespace */
  checker(errhp,
           OCISubscriptionRegister(svchp, &subscrhp, 1, errhp, OCI_DEFAULT));
 
  /* Multiple queries can now be associated with the subscription */
 
    subhandle1 = subscrhp;
 
 
    printf("Registering query : %s\n", (const signed char *)query_text1);
    /* Prepare the statement */
    checker(errhp, OCIStmtPrepare (stmthp, errhp, query_text1, 
            (ub4)strlen((const signed char *)query_text1), OCI_V7_SYNTAX,
            OCI_DEFAULT));
 
    checker(errhp,
           OCIDefineByPos(stmthp, &defnp1,
                  errhp, 1, (dvoid *)outstr, MAXSTRLENGTH * sizeof(char),
                  SQLT_STR, (dvoid *)0, (ub2 *)0, (ub2 *)0, OCI_DEFAULT));
    checker(errhp,
           OCIDefineByPos(stmthp, &defnp2,
                     errhp, 2, (dvoid *)&empno, sizeof(empno),
                     SQLT_INT, (dvoid *)0, (ub2 *)0, (ub2 *)0, OCI_DEFAULT));
    checker(errhp,
           OCIDefineByPos(stmthp, &defnp3,
                      errhp, 3, (dvoid *)&dname, sizeof(dname),
                     SQLT_STR, (dvoid *)0, (ub2 *)0, (ub2 *)0, OCI_DEFAULT));
 
    /* Associate the statement with the subscription handle */
    OCIAttrSet (stmthp, OCI_HTYPE_STMT, subscrhp, 0,
              OCI_ATTR_CHNF_REGHANDLE, errhp);
 
    /* Execute the statement, the execution performs object registration */
    checker(errhp, OCIStmtExecute (svchp, stmthp, errhp, (ub4) 1, (ub4) 0,
                 (CONST OCISnapshot *) NULL, (OCISnapshot *) NULL ,
                 OCI_DEFAULT));
    fflush(stdout);
 
    OCIAttrGet(stmthp, OCI_HTYPE_STMT, &qid, (ub4 *)0,
                OCI_ATTR_CQ_QUERYID, errhp);
    printf("Query Id %d\n", qid);
 
  /* commit */
  checker(errhp, OCITransCommit(svchp, errhp, (ub4) 0));
 
}
 
static void cleanup(envhp, svchp, srvhp, errhp, usrhp)
OCIEnv *envhp;
OCISvcCtx *svchp;
OCIServer *srvhp;
OCIError *errhp;
OCISession *usrhp;
{
  /* detach from the server */
  checker(errhp, OCISessionEnd(svchp, errhp, usrhp, OCI_DEFAULT));
  checker(errhp, OCIServerDetach(srvhp, errhp, (ub4)OCI_DEFAULT));
 
  if (usrhp)
    (void) OCIHandleFree((dvoid *) usrhp, (ub4) OCI_HTYPE_SESSION);
  if (svchp)
    (void) OCIHandleFree((dvoid *) svchp, (ub4) OCI_HTYPE_SVCCTX);
  if (srvhp)
    (void) OCIHandleFree((dvoid *) srvhp, (ub4) OCI_HTYPE_SERVER);
  if (errhp)
    (void) OCIHandleFree((dvoid *) errhp, (ub4) OCI_HTYPE_ERROR);
  if (envhp)
    (void) OCIHandleFree((dvoid *) envhp, (ub4) OCI_HTYPE_ENV);
 
}

CQN登録の問合せ

登録のQOSオプションなど、すべての登録に関するトップレベルの情報を確認するには、静的データ・ディクショナリ・ビュー*_CHANGE_NOTIFICATION_REGSを問い合せます。

たとえば、クライアントの登録IDと通知を受信するオブジェクトのリストを取得できます。HRの登録IDと表名を表示するには、次の問合せを使用します。

SELECT regid, table_name FROM USER_CHANGE_NOTIFICATION_REGS;

どの問合せがQRCNに対して登録されているかを確認するには、静的データ・ディクショナリ・ビューUSER_CQ_NOTIFICATION_QUERIESまたはDBA_CQ_NOTIFICATION_QUERIESを問い合せます。これらのビューには、問合せで使用されるバインド値に関する情報が含まれています。これらのビューでは、元の問合せのバインド値が、定数として問合せテキストに含まれます。問合せテキストは、登録済の元の問合せと同等ですが、同一ではない場合があります。

関連項目:

静的データ・ディクショナリ・ビューUSER_CHANGE_NOTIFICATION_REGSおよびDBA_CQ_NOTIFICATION_QUERIESの詳細は、『Oracle Databaseリファレンス』を参照してください。

通知の解析

トランザクションのコミット時に、データベースでは登録済オブジェクトがトランザクション中に変更されたかどうかが判別されます。変更されたと判別された場合、登録で指定されている通知ハンドラが実行されます。

トピック:

CQ_NOTIFICATION$_DESCRIPTORオブジェクトの解析

CQN登録で通知が生成されると、CQ_NOTIFICATION$_DESCRIPTORオブジェクトがデータベースから通知ハンドラに渡されます。通知ハンドラでは、データベース変更の詳細をCQ_NOTIFICATION$_DESCRIPTORオブジェクトの属性から検出できます。

SQL*Plusでは、SYSとして接続し、次の文を実行するとこれらの属性を表示できます。

DESC CQ_NOTIFICATION$_DESCRIPTOR

表18-4に、CQ_NOTIFICATION$_DESCRIPTORの属性の要約を示します。

表18-4 CQ_NOTIFICATION$_DESCRIPTORの属性

属性 説明:

REGISTRATION_ID

登録時に戻された登録ID。

TRANSACTION_ID

変更を行ったトランザクションのID。

DBNAME

通知が生成されたデータベースの名前。

EVENT_TYPE

通知をトリガーするデータベース・イベント。たとえば、属性には様々なデータベース・イベントに対応する次の定数を含めることができます。

  • EVENT_NONE

  • EVENT_STARTUP(インスタンスの起動)

  • EVENT_SHUTDOWN(インスタンスの停止 - Oracle RACの場合は最後のインスタンスの停止)

  • EVENT_SHUTDOWN_ANY(Oracle RACの場合は任意のインスタンスの停止)

  • EVENT_DEREG(登録の削除)

  • EVENT_OBJCHANGE(登録済の表に対する変更)

  • EVENT_QUERYCHANGE(登録済の結果セットに対する変更)

NUMTABLES

変更された表の数。

TABLE_DESC_ARRAY

このフィールドは、OCN登録の場合にのみ存在します。QRCN登録の場合はNULLになります。

EVENT_TYPEEVENT_OBJCHANGEの場合は、タイプCQ_NOTIFICATION$_TABLEの表変更記述子のVARRAYとなり、それぞれが変更された表に対応します。CQ_NOTIFICATION$_TABLEの属性は、表18-5を参照してください。

前述以外の場合はNULLになります。

QUERY_DESC_ARRAY

このフィールドは、QRCN登録の場合にのみ存在します。OCN登録の場合はNULLになります。

EVENT_TYPEEVENT_QUERYCHANGEの場合は、タイプCQ_NOTIFICATION$_QUERYの結果セット変更記述子のVARRAYとなり、それぞれが変更された結果セットに対応します。CQ_NOTIFICATION$_QUERYの属性は、表18-6を参照してください。

前述以外の場合はNULLになります。

CQ_NOTIFICATION$_TABLEオブジェクトの解析

CQ_NOTIFICATION$_DESCRIPTORタイプには、タイプCQ_NOTIFICATION$_TABLEの表記述子のVARRAYを保持する属性TABLE_DESC_ARRAYが含まれています。

SQL*Plusでは、SYSとして接続し、次の文を実行するとこれらの属性を表示できます。

DESC CQ_NOTIFICATION$_TABLE

表18-5に、CQ_NOTIFICATION$_TABLEの属性の要約を示します。

表18-5 CQ_NOTIFICATION$_TABLEの属性

属性 指定内容

OPFLAGS

変更があった表に対して実行された操作のタイプ。たとえば、属性には様々なデータベース操作に対応する次の定数を含めることができます。

  • ALL_ROWSは、DELETE *のように表全体に変更があるか、行レベルの粒度の情報が要求されないか、通知に使用できず、受信者は表全体が変更されたとみなす必要があることを示します。

  • UPDATEOPは更新を示します。

  • DELETEOPは削除を示します。

  • ALTEROPALTER TABLEを示します。

  • DROPOPDROP TABLEを示します。

  • UNKNOWNOPは不明な操作を示します。

TABLE_NAME

変更があった表の名前。

NUMROWS

変更があった行数。

ROW_DESC_ARRAY

タイプCQ_NOTIFICATION$_ROWの行記述子のVARRAY(表18-7を参照)。opflagsALL_ROWSが設定されていた場合、ROW_DESC_ARRAYメンバーはNULLです。

CQ_NOTIFICATION$_QUERYオブジェクトの解析

CQ_NOTIFICATION$_DESCRIPTORタイプには、タイプCQ_NOTIFICATION$_QUERYの結果セット変更記述子のVARRAYを保持する属性QUERY_DESC_ARRAYが含まれています。

SQL*Plusでは、SYSとして接続し、次の文を実行するとこれらの属性を表示できます。

DESC CQ_NOTIFICATION$_QUERY

表18-6に、CQ_NOTIFICATION$_QUERYの属性の要約を示します。

表18-6 CQ_NOTIFICATION$_QUERYの属性

属性 指定内容

QUERYID

変更された問合せのID。

QUERYOP

問合せを変更した操作(EVENT_QUERYCHANGEまたはEVENT_DEREG)。

TABLE_DESC_ARRAY

タイプCQ_NOTIFICATION$_TABLEの表変更記述子のVARRAY(それぞれが結果セットの変更の原因となった変更済の表に対応)。CQ_NOTIFICATION$_TABLEの属性は、表18-5を参照してください。

CQ_NOTIFICATION$_ROWオブジェクトの解析

登録中にROWIDオプションが指定されると、CQ_NOTIFICATION$_TABLEタイプにはROW_DESC_ARRAY属性、つまり、変更された行のROWIDを含むタイプCQ_NOTIFICATION$_ROWVARRAYが設定されます。CQ_NOTIFICATION$_TABLEオブジェクトのOPFLAGSフィールドにALL_ROWSが設定されていた場合、ROWID情報は使用できません。

表18-7に、CQ_NOTIFICATION$_ROWの属性の要約を示します。

表18-7 CQ_NOTIFICATION$_ROWの属性

属性 指定内容

OPFLAGS

変更があった表に対して実行された操作のタイプ。表18-5OPFLAGSの説明を参照してください。

ROW_ID

変更された行のROWID