OCIを使用して、Oracle TimesTen In-Memory DatabaseおよびOracle TimesTen Application-Tier Database Cacheにアクセスできます。Times TenでのOracle Call Interfaceサポートの詳細は、Oracle TimesTen In-Memory Database C開発者およびリファレンス・ガイドを参照してください。
この章は、次の項目で構成されています。
連続問合せ通知を使用すると、クライアント・アプリケーションがデータベースに問合せを登録し、オブジェクトでのDMLまたはDDL変更に応じて、あるいは問合せに関連付けられた結果セット変更に応じて通知を受信できます。通知は、DMLまたはDDLトランザクションがコミットされるときにデータベースによってパブリッシュされます。
登録時に、アプリケーションは通知ハンドラを指定し、登録する一連の問合せを通知ハンドラに関連付けます。通知ハンドラは、サーバー側のPL/SQLプロシージャでも、クライアント側のC言語のコールバックでもかまいません。登録は、オブジェクト・レベルまたは問合せレベルで作成されます。登録がオブジェクト・レベルにある場合、トランザクションで登録済オブジェクトのいずれかが変更されてコミットされると常に、通知ハンドラが起動されます。登録が問合せレベルにある場合、トランザクションで問合せの結果セットが変わるような変更内容がコミットされると常に、通知ハンドラが起動されますが、変更内容が問合せの結果セットに影響しない場合、通知ハンドラは起動されません。
関連項目: この機能の概念の詳細は、『Oracle Databaseアドバンスト・アプリケーション開発者ガイド』の「連続問合せ通知の使用」を参照してください。 |
連続問合せ通知の用途の1つは、データをキャッシュし、そのキャッシュをバックエンド・データベースのためにできるだけ最新の状態で維持する必要がある中間層アプリケーションでの使用です。
通知には次の情報が含まれます。
結果セットが変更された問合せの問合せID。これは、登録が問合せの細分化レベルにあった場合です。
変更されたオブジェクトの名前または変更された行。
操作タイプ(INSERT
、UPDATE
、DELETE
、ALTER
TABLE
、DROP
TABLE
)。
変更された行のROWID
と関連するDML操作(INSERT
、UPDATE
、DELETE
)。
グローバル・データベース・イベント(STARTUP
、SHUTDOWN
)。Oracle Real Application Clusters (Oracle RAC)では、データベースは最初のインスタンスの起動時または最後のインスタンスの停止時に通知を送信します。
連続問合せ(CQ)通知固有のQOS (サービス品質フラグ)を記録するには、サブスクリプション・ハンドルOCI_HTYPE_SUBSCR
に属性OCI_ATTR_SUBSCR_CQ_QOSFLAGS
を設定します。オブジェクトの細分化レベルではなく問合せの細分化レベルで登録するようにリクエストするには、属性OCI_ATTR_SUBSCR_CQ_QOSFLAGS
にOCI_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
型です。
コール側セッションには、登録対象のすべてのオブジェクトに対するCHANGE
NOTIFICATION
システム権限およびSELECT
権限が必要です。登録はデータベースに記録される永続エンティティであり、Oracle RACのすべてのインスタンスに表示されます。登録が問合せ精度の場合、Oracle RACインスタンスで問合せ結果セットが変更され、コミットされる原因となるトランザクションによって通知が生成されます。登録がオブジェクト精度の場合、Oracle RACインスタンスに登録済のオブジェクトを変更するトランザクションによって通知が生成されます。
マテリアライズド・ビューまたは非マテリアライズド・ビューが関係する問合せは、サポートされません。
登録インタフェースでは、問合せ対象オブジェクトでの変更に対応するためのコールバックと、AQのネームスペース拡張(DBCHANGE
)を使用します。
登録を作成する手順は、次のとおりです。
OCI_EVENTS
およびOCI_OBJECT
モードで環境を作成します。
サブスクリプション・ハンドル属性OCI_ATTR_SUBSCR_NAMESPACE
をネームスペースOCI_SUBSCR_NAMESPACE_DBCHANGE
に設定します。
サブスクリプション・ハンドル属性OCI_ATTR_SUBSCR_CALLBACK
を、問合せハンドルに関連したOCIコールバックを格納するように設定します。このコールバックには、次のプロトタイプがあります。
void notification_callback (void *ctx, OCISubscription *subscrhp, void *payload, ub4 paylen, void *desc, ub4 mode);
パラメータの詳細は、「OCIでの通知コールバック」を参照してください。
必要に応じて、OCI_ATTR_SUBSCR_CTX
属性を使用してクライアント固有のコンテキストを関連付けます。
OCI_ATTR_SUBSCR_TIMEOUT
属性を設定して、ub4
タイムアウト間隔を秒単位で指定します。設定しない場合、タイムアウトはありません。
OCI_ATTR_SUBSCR_QOSFLAGS
属性に次の値を使用して、QOS (サービス品質)レベルを設定します。
OCI_SUBSCR_QOS_PURGE_ON_NTFN
フラグは、最初の通知で登録が消去されるようにします。
OCI_SUBSCR_QOS_RELIABLE
フラグは、通知が存続するようにします。ノードに障害が発生した後でも、この登録関連の無効化されたメッセージがデータベースのキューに永続的に残っているために、残存しているOracle RACのインスタンスを使用して連続問合せ通知メッセージを送受信できます。FALSE
である場合、無効化されたメッセージは高速メモリー内キューにエンキューされます。このオプションは、登録の永続性ではなく通知の永続性を指定するものです。デフォルトでは、登録は自動的に永続的になります。
OCISubscriptionRegister()をコールし、DBCHANGE
ネームスペースに新しい登録を作成します。
複数の問合せ文をサブスクリプション・ハンドルに関連付けるには、文ハンドルOCI_HTYPE_STMT
のOCI_ATTR_CHNF_REGHANDLE
属性を設定します。問合せが実行されると、登録が完了します。
状況に応じて、サブスクリプションの登録を解除します。クライアントは、パラメータとしてサブスクリプション・ハンドルを指定して、OCISubscriptionUnRegister()関数をコールできます。
サブスクリプション・ハンドルへの文ハンドルのバインドが有効なのは、問合せの初回の実行時のみです。アプリケーションで後続の実行時に同じOCI文ハンドルを使用する必要がある場合は、文ハンドルの登録ハンドル属性に値を再移入する必要があります。文ハンドルをサブスクリプション・ハンドルにバインドできるのは、文が問合せである場合のみです(実行時に判別されます)。実行の一部としてDML文が実行されると、例外が発行されます。
連続問合せ通知のサブスクリプション・ハンドル属性は、汎用属性(すべてのサブスクリプションに対して共通)と、ネームスペース固有の属性(連続問合せ通知に特有)に分けられます。
文ハンドルの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
: 通知が永続的になるように設定します。したがって、ノード障害が発生した後でも、この登録IDに関連づけられた無効化メッセージがデータベースのキューに永続的に残っているために、残存しているOracle RACクラスタのインスタンスを使用して、無効化メッセージを送受信できます。これがFALSE
である場合、無効化メッセージは高速メモリー内キューにエンキューされます。このオプションは、登録の永続性ではなく通知の永続性を指定するものです。デフォルトでは、登録は自動的に永続的になります。
パラレルの例は、「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_ROWIDS
がTRUE
に設定されていても、通知にROWID
レベルの細分性を持つ情報が含まれなくなります。登録が問合せレベルの細分性を持つ場合、このオプションは無視されます。
OCISubscriptionRegister()コールが呼び出されると、すでに作成されている登録では、前述のいずれの属性(汎用、ネームスペース固有または機能固有)も変更できなくなります。これらの属性への変更は、すでに作成されている登録には反映されませんが、新しく作成された、同じ登録ハンドルを使用する登録には反映されます。
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
OCIStmtExecute()
のコールによる登録後、文ハンドルOCI_HTYPE_STMT
のOCI_ATTR_CQ_QUERYID
属性が登録済問合せの問合せIDを取得します。
連続問合せ通知の記述子は、アプリケーションによって指定された通知コールバックの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_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
になります。
この通知記述子は、通常はDMLトランザクションまたはDDLトランザクションのコミットに応じて、無効化された問合せを記述します。属性は次のとおりです。
この通知記述子は、登録済問合せに関係する表の変更内容に関する情報を送信します。属性は次のとおりです。
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
型の行連続問合せ記述子です。
例10-1は、単純なOCIプログラム、demoquery.c
です。リストにあるコメントを参照してください。コール側セッションには、登録対象のすべてのオブジェクトに対するCHANGE
NOTIFICATION
システム権限およびSELECT
権限が必要です。
例10-1 連続問合せ通知を説明するプログラムのリスト
/* 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/HR 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 ='Adminstration' 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 *) ¬ify_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); }
OCI関数OCIDBStartup()およびOCIDBShutdown()には、Oracle Databaseを起動および停止するために最小限必要なインタフェースが用意されています。C言語のプログラムから、OCIDBStartup()
をコールする前に、サーバーに接続し、事前認証モードでSYSDBA
またはSYSOPER
セッションを開始する必要があります。インスタンスが起動していない場合はこのモードのみを使用でき、インスタンスを起動するためにのみ使用されます。OCIDBStartup()をコールすると、データベースをマウントまたはオープンせずに1つのサーバー・インスタンスが起動します。データベースをマウントまたはオープンするには、事前認証セッションを終了し、通常のSYSDBA
またはSYSOPER
セッションを開始し、適切なALTER
DATABASE
文を実行します。
データベースを停止するには、アクティブなSYSDBA
またはSYSOPER
セッションが必要です。OCI_DBSHUTDOWN_ABORT
以外のすべてのモードにおいて、OCIDBShutdown()に2つのコールを実行します。1つめのコールは、データベースへのこれ以上の接続を禁止することにより停止を開始し、次に適切なALTER
DATABASE
コマンドによりデータベースをディスマウントして閉じるためのもので、もう1つのコールは、インスタンスを停止することにより停止を終了させるためのものです。特別な環境においては、データベースをできるかぎり迅速に停止させるために、OCIDBShutdown()をOCI_DBSHUTDOWN_ABORT
モードでコールしますが、これはSQL*PlusでのSHUTDOWN
ABORT
に相当します。
これらの関数は両方とも、サーバーに対する専用接続を必要とします。ディスパッチャを介して共有サーバーに接続されている場合、データベースを起動または停止しようとすると、ORA-106
が発生します。
OCIAdmin
管理ハンドルCデータ型を使用して、インタフェースを拡張可能にします。OCIAdmin
は、ハンドル・タイプOCI_HTYPE_ADMIN
に関連付けられています。OCIAdmin
パラメータadmhp
に値を渡すかどうかは、OCIDBStartup()ではオプションで選択できますが、OCIDBShutdown()では不要です。
起動するには、SYSOPER
またはSYSDBA
としてOCI_PRELIM_AUTH
モードでデータベースに接続する必要があります。ディスパッチャを介して共有サーバーには接続できません。クライアント側のパラメータ・ファイル(pfile
)を使用するには、OCIAttrSet()を使用して管理ハンドルで属性OCI_ATTR_ADMIN_PFILE
を設定する必要があります。設定しない場合、サーバー側のパラメータ・ファイル(spfile
)が使用されます。後者の場合は、(OCIAdmin *)0
を渡します。OCIDBStartup()のコールにより、サーバー上で1つのインスタンスが起動します。
例10-2では、管理ハンドルで設定され、データベース起動操作を実行する、クライアント側のパラメータ・ファイル(pfile
)を使用するサンプル・コードを示しています。
例10-2 データベースの起動操作を実行するOCIDBStartup()のコール
... /* Example 0 - Startup: */ OCIAdmin *admhp; text *mount_stmt = (text *)"ALTER DATABASE MOUNT"; text *open_stmt = (text *)"ALTER DATABASE OPEN"; text *pfile = (text *)"/ade/viewname/oracle/work/t_init1.ora"; /* Start the authentication session */ checkerr(errhp, OCISessionBegin (svchp, errhp, usrhp, OCI_CRED_RDBMS, OCI_SYSDBA|OCI_PRELIM_AUTH)); /* Allocate admin handle for OCIDBStartup */ checkerr(errhp, OCIHandleAlloc((void *) envhp, (void **) &admhp, (ub4) OCI_HTYPE_ADMIN, (size_t) 0, (void **) 0)); /* Set attribute pfile in the admin handle (do not do this if you want to use the spfile) */ checkerr (errhp, OCIAttrSet( (void *) admhp, (ub4) OCI_HTYPE_ADMIN, (void *) pfile, (ub4) strlen(pfile), (ub4) OCI_ATTR_ADMIN_PFILE, (OCIError *) errhp)); /* Start up in NOMOUNT mode */ checkerr(errhp, OCIDBStartup(svchp, errhp, admhp, OCI_DEFAULT, 0)); checkerr(errhp, OCIHandleFree((void *) admhp, (ub4) OCI_HTYPE_ADMIN)); /* End the authentication session */ OCISessionEnd(svchp, errhp, usrhp, (ub4)OCI_DEFAULT); /* Start the sysdba session */ checkerr(errhp, OCISessionBegin (svchp, errhp, usrhp, OCI_CRED_RDBMS, OCI_SYSDBA)); /* Mount the database */ checkerr(errhp, OCIStmtPrepare2(svchp, &stmthp, errhp, mount_stmt, (ub4) strlen((char*) mount_stmt), (CONST OraText *) 0, (ub4) 0, (ub4) OCI_NTV_SYNTAX, (ub4) OCI_DEFAULT)); checkerr(errhp, OCIStmtExecute(svchp, stmthp, errhp, (ub4) 1, (ub4)0, (OCISnapshot *) NULL, (OCISnapshot *) NULL, OCI_DEFAULT)); checkerr(errhp, OCIStmtRelease(stmthp, errhp, (OraText *)0, 0, OCI_DEFAULT)); /* Open the database */ checkerr(errhp, OCIStmtPrepare2(svchp, &stmthp, errhp, open_stmt, (ub4) strlen((char*) open_stmt), (CONST OraText *)0, (ub4)0, (ub4) OCI_NTV_SYNTAX, (ub4) OCI_DEFAULT)); checkerr(errhp, OCIStmtExecute(svchp, stmthp, errhp, (ub4) 1, (ub4)0, (OCISnapshot *) NULL, (OCISnapshot *) NULL, OCI_DEFAULT)); checkerr(errhp, OCIStmtRelease(stmthp, errhp, (OraText *)0, 0, OCI_DEFAULT)); /* End the sysdba session */ OCISessionEnd(svchp, errhp, usrhp, (ub4)OCI_DEFAULT); ...
停止するには、SYSOPER
またはSYSDBA
としてデータベースに接続する必要があります。ディスパッチャを介して共有サーバーには接続できません。OCI_DBSHUTDOWN_ABORT
以外のモードで停止する場合、次の手順を使用します。
OCIDBShutdown()をOCI_DEFAULT
、OCI_DBSHUTDOWN_TRANSACTIONAL
、OCI_DBSHUTDOWN_TRANSACTIONAL_LOCAL
またはOCI_DBSHUTDOWN_IMMEDIATE
モードでコールし、これ以上の接続を禁止します。
必要なALTER
DATABASE
コマンドを使用し、データベースをクローズしてディスマウントします。
OCIDBShutdown()をOCI_DBSHUTDOWN_FINAL
モードでコールし、インスタンスを停止します。
例10-3では、データベースの順序正しい停止操作を実行する管理ハンドルで設定される、クライアント側のパラメータ・ファイル(pfile
)を使用するサンプル・コードを示しています。
例10-3 OCI_DBSHUTDOWN_FINALモードでのOCIDBShutdown()のコール
/* Example 1 - Orderly shutdown: */ ... text *close_stmt = (text *)"ALTER DATABASE CLOSE NORMAL"; text *dismount_stmt = (text *)"ALTER DATABASE DISMOUNT"; /* Start the sysdba session */ checkerr(errhp, OCISessionBegin (svchp, errhp, usrhp, OCI_CRED_RDBMS, OCI_SYSDBA)); /* Shutdown in the default mode (transactional, transactional-local, immediate would be fine too) */ checkerr(errhp, OCIDBShutdown(svchp, errhp, (OCIAdmin *)0, OCI_DEFAULT)); /* Close the database */ checkerr(errhp, OCIStmtPrepare2(svchp, &stmthp, errhp, close_stmt, (ub4) strlen((char*) close_stmt), (CONST OraText *)0, (ub4)0, (ub4) OCI_NTV_SYNTAX, (ub4) OCI_DEFAULT)); checkerr(errhp, OCIStmtExecute(svchp, stmthp, errhp, (ub4) 1, (ub4)0, (OCISnapshot *) NULL, (OCISnapshot *) NULL, OCI_DEFAULT)); checkerr(errhp, OCIStmtRelease(stmthp, errhp, (OraText *)0, 0, OCI_DEFAULT)); /* Dismount the database */ checkerr(errhp, OCIStmtPrepare2(svchp, &stmthp, errhp, dismount_stmt, (ub4) strlen((char*) dismount_stmt), (CONST OraText *)0, (ub4)0, (ub4) OCI_NTV_SYNTAX, (ub4) OCI_DEFAULT)); checkerr(errhp, OCIStmtExecute(svchp, stmthp, errhp, (ub4) 1, (ub4)0, (OCISnapshot *) NULL, (OCISnapshot *) NULL, OCI_DEFAULT)); checkerr(errhp, OCIStmtRelease(stmthp, errhp, (OraText *)0, 0, OCI_DEFAULT)); /* Final shutdown */ checkerr(errhp, OCIDBShutdown(svchp, errhp, (OCIAdmin *)0, OCI_DBSHUTDOWN_FINAL)); /* End the sysdba session */ checkerr(errhp, OCISessionEnd(svchp, errhp, usrhp, (ub4)OCI_DEFAULT)); ...
例10-4では、OCI_DBSHUTDOWN_ABORT
モードを使用する停止の例を示しています。
例10-4 OCI_DBSHUTDOWN_ABORTモードでのOCIDBShutdown()のコール
/* Example 2 - Shutdown using abort: */ ... /* Start the sysdba session */ ... checkerr(errhp, OCISessionBegin (svchp, errhp, usrhp, OCI_CRED_RDBMS, OCI_SYSDBA)); /* Shutdown in the abort mode */ checkerr(errhp, OCIDBShutdown(svchp, errhp, (OCIAdmin *)0, OCI_DBSHUTDOWN_ABORT)); /* End the sysdba session */ checkerr(errhp, OCISessionEnd(svchp, errhp, usrhp, (ub4)OCI_DEFAULT)); ...
ROWID
は、データベース内の行のグローバル一意識別子です。ROWIDは行が表に挿入されるときに作成され、行が削除されると破棄されます。ROWID
値には、重要な用途がいくつかあります。これは、表の各行の一意識別子です。また、1行にアクセスする場合に最も高速な手段であり、表の行の格納方法を示すことができます。
SELECT
...
FOR
UPDATE
文におけるROWID
の暗黙的フェッチとは、ROWID
がselect文で名前付きの列のいずれかではない場合でも、クライアント側で取得されるということです。OCIDefineByPos()
のposition
パラメータは0 (ゼロ)に設定されます。ROWID
疑似列値を取得する場合、次のホスト変数を指定できます。
SQLT_CHR
(VARCHAR2
)
SQLT_VCS
(VARCHAR
)
SQLT_STR
(NULL
文字で終了する文字列)
SQLT_LVC
(LONG
VARCHAR
)
SQLT_AFC
(CHAR
)
SQLT_AVC
(CHARZ
)
SQLT_VST
(OCI文字列)
SQLT_RDD
(ROWID
記述子)
SELECT
...
FOR
UPDATE
文では、更新対象の行を識別し、結果セットの各行をロックします。これは、1行の既存の値に基づいて更新する必要がある場合に役立ちます。その場合は、別のユーザーにより行が変更されないことを確認する必要があります。
ROWID
の値を格納する文字バッファの指定時(SQLT_STR
形式で取得する場合など)には、ROWID
を格納するのに十分なメモリーを割り当てます。ROWID
データ型とUROWID
データ型の違いを覚えておいてください。ROWID
データ型に格納できるのは物理ROWID
のみですが、UROWID
型には論理ROWID
(索引構成表の行の識別子)も格納できます。ROWID
型の最長内部長は10バイトですが、UROWID
データ型の場合は3950バイトです。
動的定義は、OCI_DYNAMIC_FETCHとして設定されたmode
によるOCIDefineByPos()
のコールと等価です。動的定義を使用すると、特定の定義ハンドルの属性を追加設定できます。実行時に起動され、取得されるフェッチ済データまたはそのピースを格納するバッファへのポインタを取得する、コールバック関数を指定します。
ROWID
の暗黙的フェッチを使用する前に、文ハンドルで属性OCI_ATTR_FETCH_ROWID
を次のように設定する必要があります。
OCIAttrSet(stmthp, OCI_HTYPE_STMT, 0, 0 , OCI_ATTR_FETCH_ROWID, errhp);
動的定義は、ROWID
の暗黙的フェッチとは互換性がありません。通常のシナリオの場合、このモードのアプリケーションでは1つの列の各行にバッファを提供できます。つまり、1つの列値がフェッチされるたびにコールバックが呼び出されます。
位置0 (ゼロ)についてOCIDefineByPos()を使用するこの機能は、データ配列を一度にユーザー・バッファにフェッチし、その各ROWID
を同時に取得することが目的です。この機能では、ROWID
がSELECT
を使用した問合せの列のいずれかではなくても、SELECT....FOR UPDATE
文によりROWID
のフェッチが可能です。データを1つずつユーザー・バッファにフェッチする場合は、既存のOCI_ATTR_ROWID
属性を使用できます。
この機能を使用してROWID
をフェッチする場合、文ハンドルのOCI_ATTR_ROWID
属性を同時に使用してROWID
を取得することはできません。特定の文ハンドルに対して一度に使用できるのは1つのみです。
例10-5のCプログラムのフラグメントを基礎として使用します。
例10-5 ROWIDの暗黙的フェッチ
#include <oci.h> int main() { ... text *mySql = (text *) "SELECT emp_name FROM emp FOR UPDATE"; text rowid[100][15] = {0}; text empName[100][15] = {0}; ... /* Set up the environment, error handle, etc. */ ... /* Prepare the statement - select ... for update. */ if (OCIStmtPrepare (select_p, errhp, mySql, strlen(mySql), OCI_NTV_SYNTAX, OCI_DEFAULT)) { printf ("Prepare failed \n"); return (OCI_ERROR); } /* Set attribute for implicit fetching of ROWIDs on the statement handle. */ if (OCIAttrSet(select_p, OCI_HTYPE_STMT, 0, 0, OCI_ATTR_FETCH_ROWID, errhp)) { printf ("Unable to set the attribute - OCI_ATTR_FETCH_ROWID \n"); return OCI_ERROR; } /* * Define the positions: 0 for getting ROWIDs and other positions * to fetch other columns. * Also, get the define conversion done implicitly by fetching * the ROWIDs in the string format. */ if (OCIDefineByPos ( select_p, &defnp0, errhp, 0, rowid[0], 15, SQLT_STR, (void *) ind, (void *) 0, (void *) 0, OCI_DEFAULT) || OCIDefineByPos(select_p, &defnp1, errhp, 1, empName[0], 15, SQLT_STR, (void *) 0, (void *) 0, (void *) 0, OCI_DEFAULT) ) { printf ("Failed to define\n"); return (OCI_ERROR); } /* Execute the statement. */ if (errr = OCIStmtExecute(svchp, select_p, errhp, (ub4) 5, (ub4) 0, (OCISnapshot *) NULL, (OCISnapshot *) NULL, (ub4) OCI_DEFAULT)) { if (errr != OCI_NO_DATA) return errr; } printf ("Column 0 \t Column 1\n"); printf ("_________ \t ________\n"); for (i =0 ;i<5 i++) { printf("%s \t %s \n", rowid[i], empName[i]); } return OCI_SUCCESS; }
OCIアプリケーションでは、OCI結果キャッシュを活用するためのクライアント・メモリーを使用して、繰返しの多い問合せの応答時間を短縮できます。
クライアント結果キャッシュにより、クライアント・メモリーにおけるSQL問合せ結果セットのクライアント側のキャッシュが可能になります。OCI結果キャッシュはOCIアプリケーションに対して完全に透過的であり、結果セット・データのキャッシュは、結果セットに影響するセッションまたはデータベースのすべての変更内容と整合性が保たれます。
この機能を使用するアプリケーションでは、キャッシュ・ヒットがある問合せのパフォーマンスが向上します。OCIでは、これらの問合せを将来実行する際に、キャッシュ内の結果を透過的に使用できます。OCIクライアント・プロセスからローカルに結果を取り出す処理は、データベース・コールの実行および問合せの再実行よりも高速で行われるため、頻繁な問合せの実行では、結果がキャッシュされている場合パフォーマンスが大幅に向上します。
OCIキャッシュを使用すると、問合せの処理で消費されていたサーバーのCPUも節減されるため、サーバーの拡張性が向上します。複数のセッションのOCI文が、スキーマ、SQLテキスト、バインド値およびセッションの設定が類似していれば、OCIプロセス・メモリー内のキャッシュされた同じ結果セットと一致することがあります。類似していなければ、サーバーに対して問合せの実行が指示されます。
クライアント結果キャッシュを使用する場合、アプリケーション・レベルでOCI文キャッシュまたはキャッシュ文を有効化する必要があります。
クライアント結果キャッシュは、OCIセッション・プーリング、OCI接続プーリング、データベース常駐接続プーリング、OCI透過的アプリケーション・フェイルオーバー(TAF)などのOCI機能と連動します。
OCIクライアント問合せ結果キャッシュの利点は次のとおりです。
結果キャッシュはクライアント側にあるため、キャッシュ・ヒットが存在すると、OCIStmtExecute()およびOCIStmtFetch2()のコールが、サーバー・ラウンドトリップを実行せずにローカルで処理されます。これにより、サーバーCPUおよびサーバーI/Oなどのサーバー・リソースにおいて、パフォーマンスが大幅に節減されます。
OCIのクライアント側の問合せ結果セット・キャッシュは、透過的かつ一貫性のあるキャッシュです。
OCIクライアントの結果キャッシュはプロセス単位であるため、複数のクライアント・セッションで一致するキャッシュ内の結果セットを同時に使用できます。
各OCIアプリケーションで独自のカスタム結果セット・キャッシュを保持する必要性が最小限になります。
キャッシュ内の結果セットのキャッシュに関する各局面(つまり、複数のスレッド、文、セッションによる同時アクセス、キャッシュ内の結果セットの無効化またはリフレッシュ、およびキャッシュ・メモリー管理)が透過的に管理されます。
OCIプロセスでサーバーに対してラウンド・トリップが発生した場合、結果セットに影響するデータベースの変更内容について、キャッシュ内の結果セットが透過的に無効化されます。
この一貫性のあるキャッシュは、OCIを使用して構築されたすべてのOCIアプリケーションおよびドライバ(JDBC-OCI、ODP.Net、OCCI、Pro*C/C++、Pro*COBOL、ODBCなど)に対して自動的に使用可能になります。
キャッシュには、サーバー・メモリーよりコストがかからないOCIクライアント・メモリーが使用されます。
クライアントのローカル・キャッシュでは、そのクライアントにより実行される問合せの局所参照性が向上します。
クライアント結果キャッシュは、アプリケーションに対していくつかの方法で有効化でき、選択した方法に基づいて、使用時の優先順位が決まります。使用方法の詳細は、「キャッシュ例の使用例」を参照してください。
SQLヒント: 結果を問合せ結果キャッシュに格納することを示すには、問合せに/*+ result_cache */
というヒントの注釈を付けます。SQLヒントを使用する方法の優先順位が最も高く、表注釈およびセッション・パラメータより優先されます。これは、単一の問合せに適用されます。アプリケーション・レベルの変更が必要です。
表注釈: デプロイ時に、ALTER
TABLE
文とCREATE
TABLE
文の結果キャッシュ・ヒントを使用して表に注釈を付けます。表注釈を使用する方法の優先順位は、SQLヒントより下で、MODE FORCE
を使用するときのセッション・パラメータより上です。これは、その表に対するすべての問合せに適用されます。アプリケーション・レベルの変更は必要ありません。
セッション・パラメータ: すべての表ですべての問合せに対して機能します。可能なかぎりこの方法を使用してください。サーバー・パラメータ・ファイル(init.ora
)でRESULT_CACHE_MODE
初期化パラメータを設定するか、ALTER
SESSION
文とALTER
SYSTEM
文でRESULT_CACHE_MODE
句を使用します。セッション・パラメータを使用する方法の優先順位は最も低く、セッション・パラメータよりSQLヒントと表注釈の方が優先されます。すべての表に適用でき、最も有効範囲が広い方法です。アプリケーション・レベルの変更は必要ありません。
読取り専用またはほぼ読取りのみのデータベース・オブジェクトの場合、アプリケーションで表および問合せに結果キャッシュ・ヒントの注釈を付けることをお薦めします。結果キャッシュが、結果のサイズが大きい問合せに関するものである場合、これらの結果では大量のキャッシュ・メモリーが使用されることがあります。
アプリケーションで指定されたバインド値の各セットにより、(同じSQLテキストに対して)異なるキャッシュ結果セットが作成されるため、これらのすべての結果セットで大量のクライアント結果キャッシュ・メモリーが使用される場合があります。
クライアント結果キャッシュが有効である場合、問合せ結果セットをクライアントまたはサーバー(あるいはその両方)にキャッシュできます。クライアント結果キャッシュは、サーバー結果キャッシュ(デフォルトで有効)が無効化されている場合でも有効化できます。
すべてのOCI文ハンドル・コールにおける最初のOCIStmtExecute()のコールは、キャッシュ内に有効な結果セットがある場合でも、必ずサーバーに対して行われます。各文ハンドルをキャッシュ内の結果セットと照合できるようにするには、OCIStmtExecute()
をコールする必要があります。アプリケーションがOCI文ハンドルについて独自の文キャッシュを持つようにするか、またはOCI文キャッシュを使用し、OCIStmtPrepare2()が一度実行されたOCI文ハンドルを戻せるようにすることをお薦めします。同一または異なるセッションからの複数のOCI文ハンドルは、キャッシュ内の同一の結果セットからデータを同時にフェッチできます。
結果セットをキャッシュするには、このキャッシュ結果セットを透過的に作成するOCIStmtExecute()または OCIStmtFetch2()のコールで、ORA-01403「データが見つかりません」
エラーが戻されるまで行をフェッチする必要があります。ローカルにキャッシュされた結果セットと一致する後続のOCIStmtExecute()またはOCIStmtFetch2()のコールでは、最後までフェッチする必要はありません。
RESULT_CACHE_MODE
サーバー初期化パラメータがFORCE
に設定されている場合を除き、SQLヒントによりキャッシュする問合せを明示的に指定する必要があります。OCIStmtPrepare()およびOCIStmtPrepare2()コールに渡されるSQLテキストで、SQLの/*+ result_cache */
または/*+ no_result_cache */
ヒントを設定する必要があります。
ALTER
TABLE
およびCREATE
TABLE
文により、表に結果キャッシュ・モードの注釈を付けることができます。後の項で説明するとおり、セッション・パラメータもあります。表注釈とセッション・パラメータのマトリックスでは、その表に対する問合せの有効な結果キャッシュ・モードを示しています。SQLヒントは表注釈およびセッション・パラメータに優先するので注意してください。構文は次のとおりです。
CREATE|ALTER TABLE [<schema>.]<table> ... [RESULT_CACHE (MODE {FORCE|DEFAULT})]
CREATE
TABLE
の例を示します。ここでは表の列を定義しています。
CREATE TABLE foo (a NUMBER, b VARCHAR2(20)) RESULT_CACHE (MODE FORCE);
次にALTER
TABLE
の例を示します。
ALTER TABLE foo RESULT_CACHE (MODE DEFAULT);
ALTER TABLE
文は、これらの表を使用した(サーバー結果キャッシュの)文または問合せブロックの結果が結果キャッシュに格納されるよう、これらの表に注釈を付けるために使用されます。指定の問合せにSQLヒント/*+ result_cache /
または/*+ no_result_cache */
がある場合、またはパラメータRESULT_CACHE_MODE
がFORCE
に設定されている場合、ヒントまたはセッション変数は表注釈より優先されます。
結果キャッシュに格納するすべての表に注釈を付ける必要があります。これで、単一表を選択したか結合を含むかにかかわらず、キャッシュ・ヒントを含むこれらの表についてのすべてのSQL問合せに対し、キャッシュの価値があることを前提にキャッシュが検討されます。
関連項目:
|
表10-1に、結果キャッシュ注釈のモード値をまとめています。
DBAビューDBA_TABLES
、USER_TABLES
およびALL_TABLES
のRESULT_CACHE
列には、その表の結果キャッシュ・モードの注釈が表示されます。表に注釈が付けられていない場合、DEFAULT
と表示されます。
emp
表に、ALTER
TABLE
emp
RESULT_CACHE
(MODE
FORCE
)の注釈が付いているとします。
そして、次の問合せをセッションで実行します。
SELECT
table_name
, result_cache
FROM
user_tables
出力は次のとおりです。
TABLE_NAME RESULT_CACHE ---------- ------------ EMP FORCE FOO DEFAULT
出力は、次の文を使用して、FOO
表に注釈が付いているか、付いていないかを示します。
ALTER TABLE foo RESULT_CACHE (MODE DEFAULT);
関連項目: これらのDBAビューのRESULT_CACHE 列の詳細は、『Oracle Databaseリファレンス』を参照してください。 |
RESULT_CACHE_MODE
パラメータにより、問合せで複数の表間の結果キャッシュ・モードを決定できます。この句をALTER
SESSION
およびALTER
SYSTEM
文内またはサーバー・パラメータ・ファイル(init.ora
)内で使用し、結果キャッシュを決定します。
関連項目: RESULT_CACHE_MODE の詳細は、『Oracle Databaseリファレンス』を参照してください。 |
SQL問合せレベルの結果キャッシュ・ヒントは、セッション・パラメータRESULT_CACHE_MODE
および結果キャッシュ表注釈より優先されます。さらに、表注釈FORCE
は、表10-2に示すように、セッション・パラメータMANUAL
に優先します。表10-2では、セッション・パラメータRESULT_CACHE_MODE
のモード(MANUAL
およびFORCE
)を、比較可能な表注釈モードと比較し、効果的な結果キャッシュ・モードを示しています。
表10-2 有効な結果キャッシュ表モード
RESULT_CACHE_MODEパラメータ | MANUAL | FORCE |
---|---|---|
表注釈 = |
|
|
表注釈 = |
|
|
有効なモードがFORCE
の場合、実際のキャッシュはクライアントとサーバーのキャッシュの内部制限、問合せキャッシュの意味(たとえば、問合せにSYSDATE
が存在しない)、およびキャッシュで使用可能な領域によって異なります。これはSQL問合せヒント/*+ result_cache */
でも同様で、単なるヒントです。問合せが実際にキャッシュされることは意味しません。表注釈DEFAULT
は結果キャッシュが表レベルでは決定されないことを示し、セッション・パラメータのMANUALモードは、SQLヒントを優先するために問合せに注釈する必要があることを示しているため、実質的にこれらは、この設定については同等の方法ということになります。
次に、SQLヒントが表注釈とセッション・パラメータに優先する場合を示すいくつかの使用例を示します。
emp
表にALTER
TABLE
emp
RESULT_CACHE
(MODE
FORCE
)と注釈が付けられており、セッション・パラメータが設定されていない(デフォルト値MANUAL
が指定されている)場合、emp
表の問合せについて問合せキャッシュが検討されることを意味します。
例で、SQL問合せがSELECT
/*+ no_result_cache */
empno
FROM
emp
である場合、この問合せはキャッシュされません。これは、SQLヒントが表注釈およびセッション・パラメータより優先されるためです。
emp
表に注釈が付けられていないか、ALTER
TABLE
emp
RESULT_CACHE
(MODE
DEFAULT
)と注釈が付けられており、セッション・パラメータが設定されていない(デフォルト値MANUAL
が指定されている)場合、問合せがキャッシュされないことを意味します。
例で、SQL問合せにSELECT
/*+ result_cache */
*
FROM
emp
というヒントが付いている場合、この問合せについて問合せキャッシュが検討されます。
表注釈がなくSQL問合せヒントがないが、セッション・パラメータまたはシステム・パラメータがFORCE
に設定されている場合、あらゆる表のすべての問合せについて問合せキャッシュが検討されます。
関連項目: キャッシュの詳細は、『Oracle Database SQL言語リファレンス』を参照してください。 |
結果キャッシュ・ヒントが指定されている場合でも、OCIクライアントにキャッシュされない問合せがあります。このような問合せは、サーバー結果キャッシュ機能が有効な場合、データベースにキャッシュされます(詳細は、『Oracle Database概要』のSQL問合せに関する説明を参照してください)。SQL問合せに次のいずれかが含まれる場合、その問合せの結果セットはOCIクライアント結果キャッシュにキャッシュされません。
リモート・オブジェクト
選択リストの複合型
スナップショットベースの問合せまたはフラッシュバック問合せ
シリアライズ可能の読取り専用トランザクション内またはフラッシュバック・セッション内で実行される問合せ
PL/SQLファンクションが含まれる問合せ
表で仮想プライベート・データベース(VPD)ポリシーが有効となっている問合せ
クライアント・キャッシュでは、キャッシュ内の結果セットに影響する可能性のあるセッションの状態またはデータベースの変更内容と結果セットの一貫性が透過的に保たれます。
トランザクションにより、そのキャッシュ結果の作成に使用されたデータベース・オブジェクトのデータまたはメタデータが変更された場合、後続のサーバーへのラウンドトリップ時にOCIクライアントに無効化されたデータが送信されます。OCIアプリケーションで一定時間にわたりデータベース・コールが行われない場合、こうした無効化の有無を確認するため、クライアント・キャッシュの遅延設定により次のOCIStmtExecute()
コールによるデータベース・コールが強制的に実行されます。
データベースの無効化に関連するキャッシュ内の結果セットはただちに無効化され、後続のOCIStmtExecute()コールはこうした結果セットと照合されません。現在これらのキャッシュ内の結果セットからフェッチしているOCI文ハンドルは、こうした無効化データを受信した際に、この(無効化された)キャッシュ内の結果セットから引き続きフェッチできます。
キャッシュ内に使用可能な領域がある場合、プロセスによる次回のOCIStmtExecute()コールにより、新規結果セットをキャッシュできます。OCIクライアント結果キャッシュでは、未使用のメモリーが定期的に再生されます。
セッションにオープン・トランザクションがある場合、OCIでは、このトランザクションで変更されたデータベース・オブジェクトを参照する問合せが、クライアント・キャッシュではなくサーバーに対して行われます。
一貫性を保つこのメカニズムにより、OCIキャッシュはコミットされたデータベースの変更に常に近い状態になります。キャッシュできない問合せ(DML、OCILob
コールなど)のため、データベース・ラウンドトリップに必要なコールをOCIアプリケーションで頻繁に実行する場合、これらのコールによりクライアント・キャッシュがデータベースの変更を反映して透過的に最新の状態に保たれます。
表が変更されるとき、トリガーにより別の表が変更されることがあります。OCIクライアント結果キャッシュは、そのような変更すべてに敏感です。
セッション状態が変わる、たとえばNLSセッション・パラメータが変更されると、問合せ結果が異なる場合があります。OCI結果キャッシュはこのような変化の影響を受けやすく、それ以降に問合せを実行すると、正しい問合せ結果セットが返されます。現在のキャッシュ結果セットは、一致するプロセスの他のセッションで維持されます(無効化されない)。それ以外の場合、これらの結果セットはしばらくして「支配」状態になります。新しいセッション状態に対応して、新しい結果セットがキャッシュされます。
アプリケーションでそのようなデータベースおよびセッションの変更をすべて追跡する必要がある場合、面倒で、エラーが発生する可能性があります。したがって、OCI結果キャッシュでは、データベースまたはセッションのどの変更についても、結果セットの一貫性を透過的に維持します。
OCIクライアント結果キャッシュでは、クライアントにおけるスレッドのサポートは必要ありません。
クライアント結果キャッシュには、デプロイメント時設定に関するサーバー初期化パラメータおよびクライアント構成パラメータがあります。
サーバー初期化パラメータは次のとおりです。
CLIENT_RESULT_CACHE_SIZE
デフォルト値は0(ゼロ)であり、クライアント・キャッシュ機能が無効であることを示します。クライアント結果キャッシュ機能を有効にするには、サイズを32768バイト(32KB)以上に設定してください。これは、クライアントのプロセス単位の結果セット・キャッシュの最小サイズです。すべてのOCIクライアント・プロセスは、この最小サイズを取得します。CLIENT_RESULT_CACHE_SIZE
初期化パラメータを使用してこの機能をサーバーで有効化している場合にかぎり、sqlnet.ora
構成パラメータOCI_RESULT_CACHE_MAX_SIZE
でこの値をオーバーライドできます。
現在のデフォルト最大サイズを調べるには、CLIENT_RESULT_CACHE_SIZE
パラメータの値を表示します。この最大サイズを大きくするには、CLIENT_RESULT_CACHE_SIZE
を設定します。ただし、CLIENT_RESULT_CACHE_SIZE
は静的なパラメータであるため、ALTER SYSTEM
文を使用する場合には、SCOPE = SPFILE
句を含める必要があり、このパラメータに対する変更を有効にするにはデータベースの再起動が必要です。
クライアント結果キャッシュ機能がサーバーで無効化されている場合、クライアント構成パラメータOCI_RESULT_CACHE_MAX_SIZE
は無視され、クライアントではクライアント結果キャッシュを有効化できないことに注意してください。
設定可能なキャッシュ・サイズの最小値は次のようになります。
(使用可能なクライアント・メモリー)+
((キャッシュされる見込み結果セット数)
*(結果セット内の1行の平均サイズ)
* (結果セット内の平均行数))
CLIENT_RESULT_CACHE_LAG
初期化パラメータCLIENT_RESULT_CACHE_LAG
を使用すると、結果セットに影響するデータベース内の変更からクライアント結果キャッシュが遅延可能な最大時間をミリ秒で指定できます。デフォルトは3000ミリ秒です。
CLIENT_RESULT_CACHE_LAG
パラメータの値を表示すると、現在の遅延を確認できます。この値を変更するには、CLIENT_RESULT_CACHE_LAG
を設定します。ただし、CLIENT_RESULT_CACHE_LAG
は静的なパラメータであるため、ALTER SYSTEM
文を使用する場合には、SCOPE = SPFILE
句を含める必要があり、このパラメータに対する変更を有効にするにはデータベースの再起動が必要です。
表注釈。オプションです。デプロイメント時に、結果キャッシュについて読取り専用またはほぼ読取りのみの表に注釈を付けることができます。アプリケーションレベルの変更は不要です。SQL結果キャッシュ・ヒントが指定されている場合、表注釈がオーバーライドされるので注意してください。詳細は、『Oracle Database SQL言語リファレンス』を参照してください。
compatible
Oracle Databaseで互換性を維持する必要のあるリリースを指定します。クライアント結果キャッシュを有効にするには、このパラメータを11.0.0.0以上に設定する必要があります。ビューでクライアント・キャッシュを使用する場合、compatible
を11.2.0.0以上に設定する必要があります。
クライアント構成ファイルはオプションであり、サーバーのinit.ora
初期化ファイル内で設定されているキャッシュ・パラメータを上書きします。これらのパラメータは、sqlnet.ora
ファイルに含まれます。クライアント構成には、次のオプション・パラメータを使用できます。
OCI_RESULT_CACHE_MAX_SIZE
(オプション): プロセス単位の問合せキャッシュの最大サイズ(バイト数)。クライアントのsqlnet.ora
ファイルで32768未満のサイズを指定すると、このsqlnet.ora
ファイルを読み取るクライアント・プロセスのクライアント結果キャッシュ機能が無効になります。
OCI_RESULT_CACHE_MAX_RSET_SIZE
(オプション): プロセス単位の問合せキャッシュ内の結果セットの最大サイズ(バイト数)。
OCI_RESULT_CACHE_MAX_RSET_ROWS
(オプション): プロセス単位の問合せキャッシュ内の結果セットの最大サイズ(行数)。
クライアントではキャッシュの遅延を設定できないので注意してください。
OCIクライアントからの既存のラウンドトリップでは、OCIからクライアント・キャッシュに関する統計がサーバーに対して定期的に送信されます。これらの統計は、CLIENT_RESULT_CACHE_STATS$
ビューに格納されます。正常にキャッシュされた結果セット数、キャッシュ・ヒット数およびキャッシュ内の無効化された結果セット数などの情報がここに格納されます。問合せのキャッシュ・ミスの数は、クライアント結果キャッシュ統計内の「作成数」の数以上になります。より正確なキャッシュ・ミス数は、サーバーの自動ワークロード・リポジトリ(AWR)レポートに示されるサーバー実行数と等しくなります。
関連項目:
|
この後の各項では、クライアント結果キャッシュの検証実行について詳しく説明します。
まず、問合せに結果キャッシュ・ヒントを付け加えることによるパフォーマンスの向上を確認するには、/*+ result_cache */
ヒントなしで問合せ実行にかかる時間を測定します。次に、問合せに/*+ result_cache */
ヒントを追加して、再び時間を測定します。その時間差が、パフォーマンスの向上分です。
v$mystat
ビューに問い合せます。このビューに問い合せるには、権限が必要です。次の2つの問合せを実行します。
問合せ1: v$mystat
からクライアントとの間のSQL*Netラウンドトリップ数を測定します。
問合せ2: SQL結果キャッシュ・ヒントなしで、クライアントとの間のSQL*Netラウンドトリップ数を測定します。
問合せ2と問合せ1の差が、クライアント結果キャッシュを有効化せずに、通常必要なラウンドトリップ数です。
この計算では、問合せ1の問合せ自体が数回のラウンドトリップ(約2回)を行うことに注意してください。
問合せに結果キャッシュ・ヒントを追加するか、emp
表に対する問合せにFORCE
表注釈を追加して再度問合せを実行すると、問合せ2と問合せ1の差は大幅に小さくなります。
v$sqlarea
ビューに問い合せます。このビューに問い合せるには、権限が必要です。
次のSQL文を実行します。
SELECT COUNT(*) FROM emp
このSQL文を数回再実行します。
その後、v$sqlarea
から'% from emp
';のようなsql_text
で選択実行、フェッチ、parse_callsを問い合せます。
次に、emp
の結果キャッシュ表ヒントを問合せに追加します。
この問合せを数回再実行します。
クライアント・キャッシュにより、column1、column2の値は少なくなります。
前述の検証は、結果キャッシュ表注釈によっても実行できることに注意してください。
クライアント側の結果キャッシュは、サーバー結果キャッシュとは別の機能です。クライアント側結果キャッシュでは、トップレベルのSQL問合せの結果がOCIクライアント・メモリーにキャッシュされますが、サーバー結果キャッシュでは、結果セットがサーバーSGAメモリーにキャッシュされます。
また、サーバー結果キャッシュでは問合せの一部をキャッシュできます。クライアント側結果キャッシュはサーバー結果キャッシュとは関係なく有効化できますが、どちらも結果キャッシュSQLヒント、表注釈およびセッション・パラメータRESULT_CACHE_MODE
を共有します。SQL問合せ結果キャッシュの詳細は、『Oracle Database概要』を参照してください。表10-3では、クライアント側結果キャッシュ、サーバー結果キャッシュまたはその両方について、特定の結果キャッシュの関連付けを、特定のパラメータ、特定のPL/SQLパッケージの実行、特定のOracle Databaseビューへの問合せに関して示しています。
表10-3 クライアント側結果キャッシュとサーバー結果キャッシュの設定
パラメータ、PL/SQLパッケージおよびデータベース・ビュー | 結果キャッシュの関連付け |
---|---|
client_result_cache_*パラメータ client_result_cache_size、 client_result_cache_lag |
クライアント結果キャッシュ |
SQLヒント/*+ result_cache */、 /*+ no_result_cache */ |
クライアント結果キャッシュ、サーバー結果キャッシュ |
sqlnet.ora OCI_RESULT_CACHE* パラメータ: OCI_RESULT_CACHE_MAX_SIZE OCI_RESULT_CACHE_MAX_RSET_SIZE OCI_RESULT_CACHE_MAX_RSET_ROWS |
クライアント結果キャッシュ |
統計ビュー: client_result_cache_stats$ |
クライアント結果キャッシュ |
result_cache_modeパラメータ |
クライアント結果キャッシュ、サーバー結果キャッシュ |
他のすべてのresult_cache*パラメータ(result_cache_max_sizeなど) |
サーバー結果キャッシュ |
パッケージDBMS_RESULT_CACHE |
サーバー結果キャッシュ |
統計ビューv$result_cache_*、gv$result_cache_* v$result_cache_statistics、gv$result_cache_memoryなど |
サーバー結果キャッシュ |
表注釈の作成 |
クライアント結果キャッシュ、サーバー結果キャッシュ |
表注釈の変更 |
クライアント結果キャッシュ、サーバー結果キャッシュ |
この機能のデモ・ファイルについては、cdemoqc.sql
、cdemoqc.c
およびcdemoqc2.c
ファイル(すべてオペレーティング・システムのdemo
ディレクトリにあります)を参照してください。
クライアント結果キャッシュを使用するには、アプリケーションをOracle Databaseリリース11.1以上のクライアント・ライブラリと再リンクし、Oracle Databaseリリース11.1以上のデータベース・サーバーに接続する必要があります。この機能は、JDBC Type IIドライバ、OCCI、Pro*C/C++およびODP.NETを含むすべてのOCIアプリケーションで使用できます。OCIドライバは、OCIStmtPrepare()およびOCIStmtPrepare2()コールに結果キャッシュ・ヒントを自動的に渡すため、キャッシュの恩恵を受けることができます。
OCIの11gリリース1 (11.1)では、障害診断が導入されました。OCIクライアント上のインシデント(問題の発生)は、ユーザーの介入なしで診断データの形式(ダンプ・ファイルまたはコア・ダンプ・ファイル)で取得されます。診断データは、インシデントについて作成された自動診断リポジトリ(ADR)サブディレクトリに格納されます。たとえば、LinuxまたはUNIXアプリケーションがNULL
ポインタ参照で失敗した場合、コア・ファイルはオペレーティング・システム・ディレクトリではなくADRホーム・ディレクトリ(存在する場合)に書き込まれます。ADRサブディレクトリ構造および出力を処理するためのユーティリティであるADRコマンド・インタプリタ(ADRCI)について、次の各項で説明します。
ADRホームは、OCIなどの特定の製品のインスタンスおよび特定のオペレーティング・システム・ユーザーに関するすべての診断データのルート・ディレクトリです。ADRホームは、ADRベースという同一のルート・ディレクトリの下でグループ化されます。
障害診断およびOracle DatabaseのADR構造については、『Oracle Database管理者ガイド』の診断データの管理に関する解説で詳しく説明されています。
ADRベースの場所は、OCIにより次の順序で決定されます。
OCIは、最初にsqlnet.ora
ファイル(存在する場合)で次のような文を検索します(LinuxまたはUNIX)。
ADR_BASE=/foo/adr
adr
(ディレクトリの名前)が存在し、OCIアプリケーションを実行して同じADRベースを共有するすべてのオペレーティング・システム・ユーザーによって書込み可能である必要があります。foo
はパス名を表します。sqlnet.ora
の場所は、ディレクトリ$TNS_ADMIN
(Windowsの場合は%TNS_ADMIN%
)で指定されます。$TNS_ADMIN
が存在しない場合は、現在のディレクトリが使用されます。ADR_BASE
が設定されており、すべてのユーザーが1つのsqlnet.ora
を共有する場合、ディレクトリadr
が存在しない、またはユーザーによる書込みができないと、OCIは検索を停止します。ADR_BASE
が設定されていない場合、OCIは検索を続行し、他の特定のディレクトリの有無をテストします。
たとえば、sqlnet.ora
にエントリADR_BASE=/home/chuck/test
が含まれる場合、ADRベースは/home/chuck/test/oradiag_chuck
であり、ADRホームは/home/chuck/test/oradiag_chuck/diag/clients/user_chuck/host_4144260688_11
のようになります。
$ORACLE_BASE
(Windowsでは%ORACLE_BASE%
)が存在します。この場合、Oracle Universal Installerを使用したデータベースのインストール時にクライアント・サブディレクトリが作成されているため、これが存在します。
たとえば、$ORACLE_BASE
が/home/chuck/obase
である場合、ADRベースは/home/chuck/obase
であり、ADRホームは/home/chuck/obase/diag/clients/user_chuck/host_4144260688_11
のようになります。
$ORACLE_HOME
(Windowsでは%ORACLE_BASE%
)が存在します。この場合、Oracle Universal Installerを使用したデータベースのインストール時にクライアント・サブディレクトリが作成されているため、これが存在します。
たとえば、$ORACLE_HOME
が/ade/chuck_l1/oracle
である場合、ADRベースは/ade/chuck_l1/oracle/log
であり、ADRホームは/ade/chuck_l1/oracle/log/diag/clients/user_chuck/host_4144260688_11
のようになります。
オペレーティング・システムのホーム・ディレクトリは、LinuxまたはUNIXでは$HOME
、Windowsでは%USERPROFILE%
です。LinuxまたはUNIXでは、ユーザーchuck
の場所は/home/chuck/oradiag_chuck
のようになります。Windowsでは、C:\Documents and Settings\chuck
の下にOracle
というフォルダが作成されます。
たとえば、Instant Clientでは、$HOME
が/home/chuck
である場合、ADRベースは/home/chuck/oradiag_chuck
であり、ADRホームは/home/chuck/oradiag_chuck/diag/clients/user_chuck/host_4144260688_11
です。
Windowsでは、アプリケーションがサービスとして動作している場合、ホーム・ディレクトリ・オプションはスキップされます。
LinuxまたはUNIXオペレーティング・システムの一時ディレクトリは/var/tmp
です。
たとえば、Instant Clientでは、$HOME
が書込み不可である場合、ADRベースは/var/tmp/oradiag_chuck
であり、ADRホームは/var/tmp/oradiag_chuck/diag/clients/user_chuck/host_4144260688_11
です。
Windowsオペレーティング・システムの一時ディレクトリは、次の順序で検索されます。
%TMP%
%TEMP%
%USERPROFILE%
Windowsシステム・ディレクトリ
これらのディレクトリの選択肢がいずれも使用不可および書込み不可である場合、ADRベースは作成されず、診断は実行されません。
関連項目: 『Oracle Database Net Servicesリファレンス』 |
ADRCIは、ADR内の診断データを表示し、インシデントおよび問題に関する情報をOracleサポートが使用できるようzipファイルに圧縮できるコマンドライン・ツールです。ADRCIは対話式に、またスクリプトから使用できます。問題が発生すると、OCIまたはクライアントで重大なエラーとなります。各問題には問題キーがあります。1つのインシデントは問題の1回の発生を意味し、数字を使用した一意のインシデントIDで識別されます。各インシデントには、属性のセット(ORA
エラー番号、エラー・パラメータ値およびその他の情報)である問題キーが割り当てられています。2つのインシデントの問題キーが一致する場合、根本的な原因は同じです。
関連項目: ADRCIの概要は、『Oracle Databaseユーティリティ』を参照してください。 |
次に、LinuxシステムでADRCIを起動し、SHOW
BASE
コマンドについてHELP
コマンドを使用し、SHOW
BASE
コマンドを-PRODUCT
CLIENT
オプション(OCIアプリケーションに必要)とともに使用する方法を示します。ADRCIコマンドは大/小文字の区別はありません。ユーザー入力は太字で表示されます。
% adrci ADRCI: Release 11.1.0.5.0 - Beta on Wed May 2 15:53:06 2007 Copyright (c) 1982, 2007, Oracle. All rights reserved. adrci> help show base Usage: SHOW BASE [-product <product_name>] Purpose: Show the current ADR base setting. Options: [-product <product_name>]: This option allows users to show the given product's ADR Base location. The current registered products are "CLIENT" and "ADRCI". Examples: show base -product client show base adrci> show base -product client ADR base is "/ade/chuck_l3/oracle/log/oradiag_chuck"
次に、SET
BASE
コマンドを記述しています。
adrci> help set base Usage: SET BASE <base_str> Purpose: Set the ADR base to use in the current ADRCI session. If there are valid ADR homes under the base, all homes will will be added to the current ADRCI session. Arguments: <base_str>: It is the ADR base directory, which is a system-dependent directory path string. Notes: On platforms that use "." to signify current working directory, it can be used as base_str. Example: set base /net/sttttd1/scratch/someone/view_storage/someone_v1/log set base . adrci> quit
ADRCI
を起動すると、デフォルトのADRベースはrdbms
サーバーに対するものとなります。 $ORACLE_HOME
は/ade/chuck_l3/oracle
に設定されます。
% adrci
ADRCI: Release 11.1.0.5.0 - Beta on Wed May 2 16:16:55 2007
Copyright (c) 1982, 2007, Oracle. All rights reserved.
ADR base = "/ade/chuck_l3/oracle/log"
OCIアプリケーションのインシデントの場合、ベースを次のようにチェックおよび設定する必要があります。
adrci> show base -product client ADR base is "/ade/chuck_l3/oracle/log" adrci> set base /ade/chuck_l3/oracle/log
Instant Clientの場合、$ORACLE_HOME
がないため、デフォルトのベースはユーザーのホーム・ディレクトリです。
adrci> show base -product client ADR base is "/home/chuck/oradiag_chuck" adrci> set base /home/chuck/oradiag_chuck adrci> show incidents ADR Home = /home/chuck/oradiag_chuck/diag/clients/user_chuck/host_4144260688_11: ************************************************************************* INCIDENT_ID PROBLEM_KEY CREATE_TIME ------------------------------------------------------------------------- 1 oci 24550 [6] 2007-05-01 17:20:02.803697 -07:00 1 rows fetched adrci>
診断能力を無効にするには、sqlnet.ora
で次のパラメータを設定し(デフォルトはTRUE
)、診断をオフにします。
DIAG_ADR_ENABLED=FALSE DIAG_DDE_ENABLED=FALSE
OCIシグナル・ハンドラをオフにし、標準オペレーティング・システムの障害処理を再度有効化するには、sqlnet.ora
で次のパラメータを設定します。
DIAG_SIGHANDLER_ENABLED=FALSE
前述のように、sqlnet.ora
でADR_BASE
を使用し、ADRベースの場所を設定します。
Oracle Databaseクライアントには、重要なエラーが検出された際に診断情報をダンプする機能など、問題を診断するための高度な機能が備えられています。デフォルトでは、これらのダンプは、使用可能な情報の小規模なサブセットに制限されており、アプリケーション・データはダンプされません。ただし、大部分のインストールでは、ダンプ・ファイルに対して安全な場所が構成され、このログのプライバシが確保されます。この場合は、フル・ダンプをオンにすることをお薦めします。これにより、問題の解決速度が大幅に向上します。フル・ダンプを有効にするには、Oracle Databaseクライアント・インストールで使用したsqlnet.ora
ファイルに次の行を追加します。
DIAG_RESTRICTED=FALSE
診断機能が正しく機能していることを確認するには、次のようにします。
最新のクライアント・ライブラリを使用するように、アプリケーションをアップグレードします。
アプリケーションを起動します。
アプリケーションのTNS_ADMIN
ディレクトリにあるsqlnet.log
ファイルに、診断機能を起動できなかったこと(通常、これは無効なディレクトリ名または権限が原因です)を示すエラー・メッセージがないか調べます。
関連項目:
|
Oracle Databaseリリース11.2以上では、クライアントとサーバーで異なるバージョンのタイム・ゾーン・ファイルを使用できます。この動作モードはOracle Database 11.2より前のリリースではサポートされていませんでした。このような混在モードで作業するには、クライアントとサーバーの両方が11.2以上であることが必要です。この項では、このようなモードで作業する場合に発生する問題について説明します。これらの問題を回避するには、クライアントとサーバーで同じバージョンのタイム・ゾーン・ファイルを使用します。
クライアントとサーバーで異なるバージョンのタイムゾーン・ファイルを使用している場合、次の動作が見られます。異なるバージョンのタイムゾーン・ファイルを使用すると、TIMESTAMP
WITH
TIMEZONE
(TSTZ
)データ型の値の処理にのみ影響が出ることに注意してください。
次に示すOCIの日時および時間隔のAPIでは、入力パラメータがTSTZ
型の場合、無条件にエラーが発生します。これは、これらの操作が、データベースと同期していないクライアント上のローカル・タイムゾーン・ファイルに依存するためです。そのような構成で計算を続けると、クライアント層とデータベース層間で計算の一貫性がなくなる可能性があります。
OCIDateTimeCompare() OCIDateTimeConstruct() OCIDateTimeConvert() OCIDateTimeSubtract() OCIIntervalAdd() OCIIntervalSubtract() OCIIntervalFromTZ() OCIDateTimeGetTimeZoneName() OCIDateTimeGetTimeZoneOffset()Foot 1 OCIDateTimeSysTimeStamp()
TSTZ
値の取得または変更時に、パフォーマンスが低下します。パフォーマンスの低下は、異なるバージョンのタイムゾーン・ファイルを使用するクライアントとサーバーの補正に、追加の変換が必要であることから生じます。
より新しいタイムゾーン・ファイルで新規タイムゾーン・リージョンが定義される場合、新規タイムゾーン・リージョンを認識しないバージョンのタイムゾーン・ファイルを持つノードで、新規リージョンに属するTIMESTAMP
WITH
TIMEZONE
値に対してエラーが発生する可能性があります。
TSTZ
型属性を含む不透明型またはXMLType
のインスタンス、あるいはその両方を操作するアプリケーションでは、データ損失を避けるために、クライアントとサーバーで同じバージョンのタイムゾーン・ファイルを使用する必要があります。
関連項目: タイムゾーン・ファイルとタイムスタンプのタイムゾーン・データによるアップグレードの詳細は、『Oracle Databaseグローバリゼーション・サポート・ガイド』を参照してください。 |
脚注の凡例
脚注 1: クライアントとサーバーのタイム・ゾーン・ファイルが一致しない(リージョンが同期化されていない)場合は、ORA-01805
エラーが戻されます。リージョン・タイム・ゾーン値が同じ(UTCで同じインスタントを示す)場合は、TIME ZONE
オフセットが異なっていてもOCI_SUCCESS
が戻されます。