表17-6は、この項で説明しているトランザクション関数を示しています。
表17-6 トランザクション関数
関数 | 用途 |
---|---|
|
サービス・コンテキスト上のトランザクションをコミットします。 |
|
サービス・コンテキストからトランザクションを連結解除します。 |
|
準備したグローバル・トランザクションを放棄します。 |
|
単一セルに複数のブランチがあるトランザクションを準備します。 |
|
グローバル・トランザクションをコミットのために準備します。 |
|
トランザクションをロールバックします。 |
|
サービス・コンテキスト上のトランザクションを開始します。 |
パラメータ
サービス・コンテキスト・ハンドルです。
エラー発生時の診断情報のためにOCIErrorGet()に渡すエラー・ハンドルです。
グローバル・トランザクションの1フェーズ・コミットを最適化するためのフラグです。
OCI_DEFAULT
- トランザクションが分散型でない場合、flagsパラメータは無視され、その値としてOCI_DEFAULT
を渡すことができます。
OCI_TRANS_TWOPHASE
- 2フェーズ・コミットでは、グローバル・トランザクションを管理しているOCIアプリケーションは、flagsに対してこの値を渡す必要があります。デフォルトは1フェーズ・コミットです。
OCI_TRANS_WRITEIMMED
- (インメモリー)REDOバッファをオンラインREDOログに書き込むためのI/OがLGWR (バックグラウンドのログ・ライター・プロセス)によって開始されます。IMMEDIATE
とは、メッセージをLGWRに送信してLGWRでメッセージを即時に処理し、トランザクションのREDOバッファを即時に書き出すことを示します。
OCI_TRANS_WRITEBATCH
- トランザクションのインメモリーREDOバッファをオンラインREDOログに書き込むためのI/OはLGWRによって発行されません。BATCH
は、LGWRがREDOバッファをバッチ処理してからバッチ全体に対してI/Oを開始することを示します。BATCH
とIMMEDIATE
の両方を指定するとエラーが発生します。デフォルトはIMMEDIATE
です。
OCI_TRANS_WRITEWAIT
- これは、コミットのREDOバッファをオンラインREDOログに書き込むようLGWRに要求し、書き込まれるまではコミットを待機させます。WAIT
とは、トランザクションに対応するインメモリーREDOバッファが永続オンラインREDOログに書き込まれて初めてコミットが終了することを示します。
OCI_TRANS_WRITENOWAIT
- これは、コミットのREDOバッファをオンラインREDOログに書き込むようLGWRに要求しますが、バッファがオンラインREDOログに書き込まれるのを待たずにコミットは終了します。NOWAIT
とは、インメモリーREDOバッファをオンラインREDOログにフラッシュせずにコミットが終了することを示します。WAIT
とNOWAIT
の両方を指定するとエラーが発生します。デフォルトはWAIT
です。
注意: OCI_TRANS_WRITENOWAIT を使用すると、警告なしでトランザクションが消失する可能性があります。トランザクションの消失は、強制停止、強制起動、およびインスタンス障害やノード障害によって、警告なしに発生します。Oracle RACシステムでは、非同期にコミットされた場合、他のインスタンスでその変更を即時に読み取れない場合があります。 |
これら最後の4つのオプションは、分散型でないトップレベルのトランザクションのコミットのみに影響し、外部協調分散トランザクションでは無視されます。これらのオプションは、指定されている制限内容に応じてOR
演算子を使用して結合できます。
コメント
サービス・コンテキストに現在対応付けられているトランザクションをコミットします。トランザクションがサーバーによるコミットが不可能なグローバル・トランザクションである場合、このコールは、トランザクションの状態をデータベースから取り出し、エラー・ハンドルを使用してユーザーに戻します。
複数のトランザクションを定義している場合、この関数は、サービス・コンテキストに現在対応付けられているトランザクションを処理します。データベースの変更時に作成される暗黙的ローカル・トランザクションのみを処理している場合は、その暗黙的トランザクションがコミットされます。
アプリケーションをオブジェクト・モードで実行している場合は、このトランザクションに対してオブジェクト・キャッシュで変更または更新されたオブジェクトもフラッシュされ、コミットされます。
正常な状況では、OCITransCommit()
は、トランザクションがコミットされたかロールバックされたかを示す状態を戻します。グローバル・トランザクションでは、トランザクションがインダウトの状態、つまり、コミットも終了もされていない状態の場合もあり得ます。この場合、OCITransCommit()
は、トランザクションの状態をサーバーから取り出します。そのステータスが戻ります。
例
例17-3は、「単純なローカル・トランザクション」に記述されている単純なローカル・トランザクションの使用方法を説明しています。
例17-3 単純なローカル・トランザクションでのOCITransCommit()の使用
int main() { OCIEnv *envhp; OCIServer *srvhp; OCIError *errhp; OCISvcCtx *svchp; OCIStmt *stmthp; void *tmp; text sqlstmt[128]; OCIEnvCreate(&envhp, OCI_DEFAULT, (void *)0, 0, 0, 0, (size_t)0, (void *)0); OCIHandleAlloc( (void *) envhp, (void **) &errhp, (ub4) OCI_HTYPE_ERROR, (size_t)0, (void **) 0); OCIHandleAlloc( (void *) envhp, (void **) &srvhp, (ub4) OCI_HTYPE_SERVER, (size_t)0, (void **) 0); OCIServerAttach( srvhp, errhp, (text *) 0, (sb4) 0, (ub4) OCI_DEFAULT); OCIHandleAlloc( (void *) envhp, (void **) &svchp, (ub4) OCI_HTYPE_SVCCTX, (size_t)0, (void **) 0); OCIHandleAlloc((void *)envhp, (void **)&stmthp, OCI_HTYPE_STMT, 0, 0); OCIAttrSet((void *)svchp, OCI_HTYPE_SVCCTX, (void *)srvhp, 0, OCI_ATTR_SERVER, errhp); OCILogon(envhp, errhp, &svchp, (text *)"HR", strlen("HR"), (text *)"HR", strlen("HR"), 0, 0); /* update hr.employees employee_id=7902, increment salary */ sprintf((char *)sqlstmt, "UPDATE EMPLOYEES SET SALARY = SALARY + 1 \ WHERE EMPLOYEE_ID = 7902"); OCIStmtPrepare(stmthp, errhp, sqlstmt, strlen((char *)sqlstmt), OCI_NTV_SYNTAX, 0); OCIStmtExecute(svchp, stmthp, errhp, 1, 0, 0, 0, 0); OCITransCommit(svchp, errhp, (ub4) 0); /* update hr.employees employee_id=7902, increment salary again, but rollback */ OCIStmtExecute(svchp, stmthp, errhp, 1, 0, 0, 0, 0); OCITransRollback(svchp, errhp, (ub4) 0); }
パラメータ
サービス・コンテキスト・ハンドルです。
エラー発生時の診断情報のためにOCIErrorGet()に渡すエラー・ハンドルです。
このパラメータ用に値OCI_DEFAULT
を渡す必要があります。
コメント
サービス・コンテキスト・ハンドルからグローバル・トランザクションを連結解除します。このコールが終了した時点で、サービス・コンテキスト・ハンドルに現在連結されているトランザクションが非活動状態になります。このトランザクションは、後でOCITransStart()をコールしながらOCI_TRANS_RESUME
のフラグ値を指定するときに再開できます。
トランザクションの連結が解除されると、トランザクションの開始時にOCITransStart()のtimeout
パラメータに指定された値を使用して、サーバーのPMON
プロセスによって削除されるまでブランチを非活動状態にしておける時間が判断されます。
注意: トランザクションが同一の認可を持っている場合、トランザクションはその連結を解除したプロセス以外のプロセスによっても再開できます。トランザクションが実際に開始される前にこの関数がコールされると、この関数は何も行いません。 |
OCITransDetach()
の使用方法を説明するコード例については、OCITransStart()の例の項を参照してください。
パラメータ
トランザクションが常駐するサービス・コンテキスト・ハンドルです。
エラー発生時の診断情報のためにOCIErrorGet()に渡すエラー・ハンドルです。
このパラメータ用にOCI_DEFAULT
を渡す必要があります。
構文
sword OCITransMultiPrepare ( OCISvcCtx *svchp, ub4 numBranches, OCITrans **txns, OCIError **errhp);
パラメータ
サービス・コンテキスト・ハンドルです。
ブランチの数を指定します。この値は、次の2つのパラメータの配列サイズでもあります。
準備するブランチのトランザクション・ハンドルの配列です。これらにはすべてOCI_ATTR_XID
が設定されます。グローバル・トランザクションIDは同じにしてください。
エラー・ハンドルの配列です。OCI_SUCCESS
が戻されない場合、このパラメータによってどのブランチがどのエラーを受信したか示されます。
パラメータ
サービス・コンテキスト・ハンドルです。
エラー発生時の診断情報のためにOCIErrorGet()に渡すエラー・ハンドルです。
このパラメータ用にOCI_DEFAULT
を渡す必要があります。
パラメータ
サービス・コンテキスト・ハンドルです。サービス・コンテキスト・ハンドル内に現在設定されているトランザクションがロールバックされます。
エラー発生時の診断情報のためにOCIErrorGet()に渡すエラー・ハンドルです。
このパラメータ用に値OCI_DEFAULT
を渡す必要があります。
コメント
カレント・トランザクション(最後のOCITransCommit()、またはOCISessionBegin()以降に実行された一連の文として定義されます)がロールバックされます。
アプリケーションがオブジェクト・モードで実行されている場合は、このトランザクションで修正または更新されたオブジェクト・キャッシュ内のオブジェクトも同様にロールバックされます。
現在アクティブでないグローバル・トランザクションをロールバックしようとすると、エラーになります。
パラメータ
サービス・コンテキスト・ハンドルです。flagsパラメータに新規トランザクションの開始が指定されている場合は、このコールの終了時点で、サービス・コンテキスト・ハンドル内のトランザクション・コンテキストが初期化されます。
OCIエラー・ハンドルです。エラーがある場合は、err
に記録され、OCI_ERROR
が戻されます。OCIErrorGet()のコールによって診断情報を取得できます。
OCI_TRANS_RESUME
が指定されたときに、トランザクションが再開できるようになるまで待機する時間(秒)です。OCI_TRANS_NEW
が指定されると、timeoutパラメータの指示する秒数間、非活動状態が続いたトランザクションは、システムによって自動的に終了します。トランザクションは、OCITransDetach()による連結解除から、OCITransStart()
による再開までの間、非活動状態になります。
新規トランザクションが開始中か、または既存のトランザクションが再開中であるかを指定します。シリアライズ可能または読取り専用ステータスも指定します。複数の値を指定できます。デフォルトでは、読取りトランザクションまたは書込みトランザクションが開始されます。フラグ値は次のとおりです。
OCI_TRANS_NEW
- 新規トランザクション・ブランチを開始します。デフォルトでは、密結合された移行可能なブランチが開始されます。
OCI_TRANS_TIGHT
- 密結合ブランチを明示的に指定します。
OCI_TRANS_LOOSE
- 疎結合ブランチを指定します。
OCI_TRANS_RESUME
- 既存のトランザクション・ブランチを再開します。
OCI_TRANS_READONLY
- 読取り専用トランザクションを開始します。
OCI_TRANS_SERIALIZABLE
- シリアライズ可能トランザクションを開始します。
OCI_TRANS_SEPARABLE
- 各コール後にトランザクションを分離します。
このフラグは、標準のトランザクションを使用してトランザクションが開始されたという警告を示します。リリース9.0.1のサーバーでは、分離されたトランザクションはサポートされていません。
コードまたはトランザクション・サービスにエラーがある場合は、エラー・メッセージが表示されます。このエラーは、すでに準備されていたトランザクションで処理を試みたことを示します。
コメント
この関数は、グローバル・トランザクションまたはシリアライズ可能トランザクションの開始を設定します。flags
パラメータに新規トランザクションの開始が指定されている場合は、このコールの終了時点で、サービス・コンテキスト・ハンドルに現在対応付けられているトランザクション・コンテキストが初期化されます。
トランザクションのXIDがトランザクション・ハンドルの属性として設定されます(OCI_ATTR_XID
)。
例
例17-4および例17-5では、OCIトランザクション・コールを使用してグローバルなトランザクションを操作する方法を説明します。例17-4に示すような、様々なブランチで動作するシングル・セッションの概念は、図8-2で示しています。
例17-4 様々なブランチで動作するシングル・セッションでのOCITransStart()の使用
int main() { OCIEnv *envhp; OCIServer *srvhp; OCIError *errhp; OCISvcCtx *svchp; OCISession *usrhp; OCIStmt *stmthp1, *stmthp2; OCITrans *txnhp1, *txnhp2; void *tmp; XID gxid; text sqlstmt[128]; OCIEnvCreate(&envhp, OCI_DEFAULT, (void *)0, 0, 0, 0, (size_t)0, (void *)0); OCIHandleAlloc( (void *) envhp, (void **) &errhp, (ub4) OCI_HTYPE_ERROR, 52, (void **) &tmp); OCIHandleAlloc( (void *) envhp, (void **) &srvhp, (ub4) OCI_HTYPE_SERVER, 52, (void **) &tmp); OCIServerAttach( srvhp, errhp, (text *) 0, (sb4) 0, (ub4) OCI_DEFAULT); OCIHandleAlloc( (void *) envhp, (void **) &svchp, (ub4) OCI_HTYPE_SVCCTX, 52, (void **) &tmp); OCIHandleAlloc((void *)envhp, (void **)&stmthp1, OCI_HTYPE_STMT, 0, 0); OCIHandleAlloc((void *)envhp, (void **)&stmthp2, OCI_HTYPE_STMT, 0, 0); OCIAttrSet((void *)svchp, OCI_HTYPE_SVCCTX, (void *)srvhp, 0, OCI_ATTR_SERVER, errhp); /* set the external name and internal name in server handle */ OCIAttrSet((void *)srvhp, OCI_HTYPE_SERVER, (void *) "demo", 0, OCI_ATTR_EXTERNAL_NAME, errhp); OCIAttrSet((void *)srvhp, OCI_HTYPE_SERVER, (void *) "txn demo", 0, OCI_ATTR_INTERNAL_NAME, errhp); /* allocate a user context handle */ OCIHandleAlloc((void *)envhp, (void **)&usrhp, (ub4) OCI_HTYPE_SESSION, (size_t) 0, (void **) 0); OCIAttrSet((void *)usrhp, (ub4)OCI_HTYPE_SESSION, (void *)"HR", (ub4)strlen("HR"), OCI_ATTR_USERNAME, errhp); OCIAttrSet((void *)usrhp, (ub4)OCI_HTYPE_SESSION, (void *)"HR", (ub4)strlen("HR"),OCI_ATTR_PASSWORD, errhp); OCISessionBegin (svchp, errhp, usrhp, OCI_CRED_RDBMS, 0); OCIAttrSet((void *)svchp, (ub4)OCI_HTYPE_SVCCTX, (void *)usrhp, (ub4)0, OCI_ATTR_SESSION, errhp); /* allocate transaction handle 1 and set it in the service handle */ OCIHandleAlloc((void *)envhp, (void **)&txnhp1, OCI_HTYPE_TRANS, 0, 0); OCIAttrSet((void *)svchp, OCI_HTYPE_SVCCTX, (void *)txnhp1, 0, OCI_ATTR_TRANS, errhp); /* start a transaction with global transaction id = [1000, 123, 1] */ gxid.formatID = 1000; /* format id = 1000 */ gxid.gtrid_length = 3; /* gtrid = 123 */ gxid.data[0] = 1; gxid.data[1] = 2; gxid.data[2] = 3; gxid.bqual_length = 1; /* bqual = 1 */ gxid.data[3] = 1; OCIAttrSet((void *)txnhp1, OCI_HTYPE_TRANS, (void *)&gxid, sizeof(XID), OCI_ATTR_XID, errhp); /* start global transaction 1 with 60-second time to live when detached */ OCITransStart(svchp, errhp, 60, OCI_TRANS_NEW); /* update hr.employees employee_id=7902, increment salary */ sprintf((char *)sqlstmt, "UPDATE EMPLOYEES SET SALARY = SALARY + 1 \ WHERE EMPLOYEE_ID = 7902"); OCIStmtPrepare(stmthp1, errhp, sqlstmt, strlen((char *)sqlstmt), OCI_NTV_SYNTAX, 0); OCIStmtExecute(svchp, stmthp1, errhp, 1, 0, 0, 0, 0); /* detach the transaction */ OCITransDetach(svchp, errhp, 0); /* allocate transaction handle 2 and set it in the service handle */ OCIHandleAlloc((void *)envhp, (void **)&txnhp2, OCI_HTYPE_TRANS, 0, 0); OCIAttrSet((void *)svchp, OCI_HTYPE_SVCCTX, (void *)txnhp2, 0, OCI_ATTR_TRANS, errhp); /* start a transaction with global transaction id = [1000, 124, 1] */ gxid.formatID = 1000; /* format id = 1000 */ gxid.gtrid_length = 3; /* gtrid = 124 */ gxid.data[0] = 1; gxid.data[1] = 2; gxid.data[2] = 4; gxid.bqual_length = 1; /* bqual = 1 */ gxid.data[3] = 1; OCIAttrSet((void *)txnhp2, OCI_HTYPE_TRANS, (void *)&gxid, sizeof(XID), OCI_ATTR_XID, errhp); /* start global transaction 2 with 90 second time to live when detached */ OCITransStart(svchp, errhp, 90, OCI_TRANS_NEW); /* update hr.employees employee_id=7934, increment salary */ sprintf((char *)sqlstmt, "UPDATE EMPLOYEES SET SALARY = SALARY + 1 \ WHERE EMPLOYEE_ID = 7934"); OCIStmtPrepare(stmthp2, errhp, sqlstmt, strlen((char *)sqlstmt), OCI_NTV_SYNTAX, 0); OCIStmtExecute(svchp, stmthp2, errhp, 1, 0, 0, 0, 0); /* detach the transaction */ OCITransDetach(svchp, errhp, 0); /* Resume transaction 1, increment salary and commit it */ /* Set transaction handle 1 into the service handle */ OCIAttrSet((void *)svchp, OCI_HTYPE_SVCCTX, (void *)txnhp1, 0, OCI_ATTR_TRANS, errhp); /* attach to transaction 1, wait for 10 seconds if the transaction is busy */ /* The wait is clearly not required in this example because no other */ /* process/thread is using the transaction. It is only for illustration */ OCITransStart(svchp, errhp, 10, OCI_TRANS_RESUME); OCIStmtExecute(svchp, stmthp1, errhp, 1, 0, 0, 0, 0); OCITransCommit(svchp, errhp, (ub4) 0); /* attach to transaction 2 and commit it */ /* set transaction handle2 into the service handle */ OCIAttrSet((void *)svchp, OCI_HTYPE_SVCCTX, (void *)txnhp2, 0, OCI_ATTR_TRANS, errhp); OCITransCommit(svchp, errhp, (ub4) 0); }
例17-5 同じトランザクションを共有する複数のブランチで動作するシングル・セッションでのOCITransStart()の使用
int main() { OCIEnv *envhp; OCIServer *srvhp; OCIError *errhp; OCISvcCtx *svchp; OCISession *usrhp; OCIStmt *stmthp; OCITrans *txnhp1, *txnhp2; void *tmp; XID gxid; text sqlstmt[128]; OCIEnvCreate(&envhp, OCI_DEFAULT, (void *)0, 0, 0, 0, (size_t)0, (void *)0); OCIHandleAlloc( (void *) envhp, (void **) &errhp, (ub4) OCI_HTYPE_ERROR, 52, (void **) &tmp); OCIHandleAlloc( (void *) envhp, (void **) &srvhp, (ub4) OCI_HTYPE_SERVER, 52, (void **) &tmp); OCIServerAttach( srvhp, errhp, (text *) 0, (sb4) 0, (ub4) OCI_DEFAULT); OCIHandleAlloc( (void *) envhp, (void **) &svchp, (ub4) OCI_HTYPE_SVCCTX, 52, (void **) &tmp); OCIHandleAlloc((void *)envhp, (void **)&stmthp, OCI_HTYPE_STMT, 0, 0); OCIAttrSet((void *)svchp, OCI_HTYPE_SVCCTX, (void *)srvhp, 0, OCI_ATTR_SERVER, errhp); /* set the external name and internal name in server handle */ OCIAttrSet((void *)srvhp, OCI_HTYPE_SERVER, (void *) "demo", 0, OCI_ATTR_EXTERNAL_NAME, errhp); OCIAttrSet((void *)srvhp, OCI_HTYPE_SERVER, (void *) "txn demo2", 0, OCI_ATTR_INTERNAL_NAME, errhp); /* allocate a user context handle */ OCIHandleAlloc((void *)envhp, (void **)&usrhp, (ub4) OCI_HTYPE_SESSION, (size_t) 0, (void **) 0); OCIAttrSet((void *)usrhp, (ub4)OCI_HTYPE_SESSION, (void *)"HR", (ub4)strlen("HR"), OCI_ATTR_USERNAME, errhp); OCIAttrSet((void *)usrhp, (ub4)OCI_HTYPE_SESSION, (void *)"HR", (ub4)strlen("HR"),OCI_ATTR_PASSWORD, errhp); OCISessionBegin (svchp, errhp, usrhp, OCI_CRED_RDBMS, 0); OCIAttrSet((void *)svchp, (ub4)OCI_HTYPE_SVCCTX, (void *)usrhp, (ub4)0, OCI_ATTR_SESSION, errhp); /* allocate transaction handle 1 and set it in the service handle */ OCIHandleAlloc((void *)envhp, (void **)&txnhp1, OCI_HTYPE_TRANS, 0, 0); OCIAttrSet((void *)svchp, OCI_HTYPE_SVCCTX, (void *)txnhp1, 0, OCI_ATTR_TRANS, errhp); /* start a transaction with global transaction id = [1000, 123, 1] */ gxid.formatID = 1000; /* format id = 1000 */ gxid.gtrid_length = 3; /* gtrid = 123 */ gxid.data[0] = 1; gxid.data[1] = 2; gxid.data[2] = 3; gxid.bqual_length = 1; /* bqual = 1 */ gxid.data[3] = 1; OCIAttrSet((void *)txnhp1, OCI_HTYPE_TRANS, (void *)&gxid, sizeof(XID), OCI_ATTR_XID, errhp); /* start global transaction 1 with 60-second time to live when detached */ OCITransStart(svchp, errhp, 60, OCI_TRANS_NEW); /* update hr.employees employee_id=7902, increment salary */ sprintf((char *)sqlstmt, "UPDATE EMPLOYEES SET SALARY = SALARY + 1 \ WHERE EMPLOYEE_ID = 7902"); OCIStmtPrepare(stmthp, errhp, sqlstmt, strlen((char *)sqlstmt), OCI_NTV_SYNTAX, 0); OCIStmtExecute(svchp, stmthp, errhp, 1, 0, 0, 0, 0); /* detach the transaction */ OCITransDetach(svchp, errhp, 0); /* allocate transaction handle 2 and set it in the service handle */ OCIHandleAlloc((void *)envhp, (void **)&txnhp2, OCI_HTYPE_TRANS, 0, 0); OCIAttrSet((void *)svchp, OCI_HTYPE_SVCCTX, (void *)txnhp2, 0, OCI_ATTR_TRANS, errhp); /* start a transaction with global transaction id = [1000, 123, 2] */ /* The global transaction is tightly coupled with earlier transactions */ /* There is not much practical value in doing this but the example */ /* illustrates the use of tightly coupled transaction branches. */ /* In a practical case, the second transaction that tightly couples with */ /* the first can be executed from a different process/thread. */ gxid.formatID = 1000; /* format id = 1000 */ gxid.gtrid_length = 3; /* gtrid = 123 */ gxid.data[0] = 1; gxid.data[1] = 2; gxid.data[2] = 3; gxid.bqual_length = 1; /* bqual = 2 */ gxid.data[3] = 2; OCIAttrSet((void *)txnhp2, OCI_HTYPE_TRANS, (void *)&gxid, sizeof(XID), OCI_ATTR_XID, errhp); /* start global transaction 2 with 90-second time to live when detached */ OCITransStart(svchp, errhp, 90, OCI_TRANS_NEW); /* update hr.employees employee_id=7902, increment salary */ /* This is possible even if the earlier transaction has locked this row */ /* because the two global transactions are tightly coupled */ OCIStmtExecute(svchp, stmthp, errhp, 1, 0, 0, 0, 0); /* detach the transaction */ OCITransDetach(svchp, errhp, 0); /* Resume transaction 1 and prepare it. This returns */ /* OCI_SUCCESS_WITH_INFO because all branches except the last branch */ /* are treated as read-only transactions for tightly coupled transactions */ OCIAttrSet((void *)svchp, OCI_HTYPE_SVCCTX, (void *)txnhp1, 0, OCI_ATTR_TRANS, errhp); if (OCITransPrepare(svchp, errhp, (ub4) 0) == OCI_SUCCESS_WITH_INFO) { text errbuf[512]; ub4 buflen; sb4 errcode; OCIErrorGet ((void *) errhp, (ub4) 1, (text *) NULL, &errcode, errbuf, (ub4) sizeof(errbuf), (ub4) OCI_HTYPE_ERROR); printf("OCITransPrepare - %s\n", errbuf); } /* attach to transaction 2 and commit it */ /* set transaction handle2 into the service handle */ OCIAttrSet((void *)svchp, OCI_HTYPE_SVCCTX, (void *)txnhp2, 0, OCI_ATTR_TRANS, errhp); OCITransCommit(svchp, errhp, (ub4) 0); }