5 プログラマ向けOracle Database ODBCドライバ

この章は、Oracle Database ODBCドライバを使用して堅牢なODBCアプリケーションを開発するプログラマを対象としています。

トピック:

5.1 接続文字列のフォーマット

次の表に、SQLDriverConnect関数コールの接続文字列引数に組み込むことができるキーワードを示します。指定されていないキーワードは、データソースのAdministratorのエントリから読み込まれます。接続文字列に指定される値はAdministratorのエントリに格納されている値を上書きします。SQLDriverConnect関数の詳細は、Microsoft ODBC 3.52 Software Development Kit and Programmers Referenceを参照してください。

表5-1 SQLDriverConnect関数コールの接続文字列引数に組み込むことができるキーワード

キーワード 意味 コメント

DSN

ODBCデータソース名

ユーザー指定の名前。

DBQ

TNSサービス名

ユーザー指定の名前。

UID

ユーザーIDまたはユーザー名

ユーザー指定の名前。

PWD

パスワード

ユーザー指定のパスワード。空のパスワードに対しては、PWD=;と指定。

DBA

データベース属性

W=書込みアクセス。

R=読取り専用アクセス。

APA

アプリケーション属性

T=スレッド・セーフティ有効。

F=スレッド・セーフティ無効。

RST

結果セット

T=結果セット有効。

F=結果セット無効。

QTO

問合せタイムアウト・オプション

T=クエリー・タイムアウト有効。

F=クエリー・タイムアウト無効。

CSR

カーソル・クローズ

T=カーソル・クローズ有効。

F=カーソル・クローズ無効。

BNF

NUMBERFLOATとしてバインド

T=NUMBERFLOATとしてバインド。

F=NUMBERNUMBERとしてバインド。

DRH

RULEヒントの無効化

T=RULEヒントを無効化。

F=RULEヒントを有効化。

BAM

バッチ自動コミット・モード

IfAllSuccessful=すべての文が成功した場合のみコミット(古い動作)。

UpToFirstFailure=最初に失敗した文までコミット(V7 ODBCの動作)。

AllSuccessful=すべての成功した文をコミット(Oracle Databaseに接続している場合のみ、その他のデータベースでは、V7と同じ動作)。

FBS

フェッチ・バッファ・サイズ

ユーザー指定の数値(バイト単位で0以上を指定)。

デフォルトは60,000バイトです。

FEN

フェイルオーバー

T=フェイルオーバー有効。

F=フェイルオーバー無効。

FRC

フェイルオーバー再試行数

ユーザー指定の数値。

デフォルト値は10です。

FDL

フェイルオーバー遅延

ユーザー指定の数値。

デフォルト値は10です。

LOB

LOB書込み

T=LOB有効。

F=LOB無効。

MTS

Microsoft Transaction Serverサポート

T=無効。

F=有効。

FWC

SQL_WCHAR強制サポート

T=強制SQL_WCHAR有効。

F=強制SQL_WCHAR無効。

EXC

EXEC構文

T=EXEC構文有効。

F=EXEC構文無効。

XSM

スキーマ・フィールド

Default=デフォルト。

Database=データベース名。

Owner=所有者名。

MDI

メタデータIDのデフォルトの設定

T=SQL_ATTR_METADATA_IDのデフォルトをSQL_TRUEに設定。

F=SQL_ATTR_METADATA_IDのデフォルトをSQL_FALSEに設定。

DPM

SQLDescribeParamの無効化

T=SQLDescribeParam無効。

F=SQLDescribeParam有効。

BTD

TIMESTAMPDATEとしてバインド

T=SQL_TIMESTAMPをOracle DATEでバインド。

F=SQL_TIMESTAMPをOracle TIMESTAMPでバインド。

NUM

数値の設定

NLS=Oracle NLS数値設定を使用(小数点および3桁区切りの決定)。

MS=Microsoft地域設定を使用。

US=US設定を使用。

ODA

OCIDescribeAny( )の使用

T=アプリケーションがREF CURSORSを戻す小さなパッケージ・プロシージャを頻繁にコールする場合にパフォーマンスを向上させるためにOCIDescribeAny()コールを使用。

F=OCIDescribeAny()を使用しない。デフォルトでは、OCIDescribeAny()値の使用はFALSEです。

STE

SQLでORAエラーを変換

Oracle Database ODBCドライバがOracleエラー・コードを変換するかどうかを指定

T=ORAエラーを変換。

F=ORAエラーを変換しない。デフォルトでは、SQLTranslateErrorsFALSEです。

TSZ

トークン・サイズ

ユーザー指定の数値。

トークン・サイズを4KB (4096バイト)から開始する最も近い1KB (1024バイト)の倍数に設定します。デフォルト・サイズは8KB (8192バイト)です。設定できる最大値は128KB (131068バイト)です。

次のキーワードが接続文字列に指定された場合、Oracle Database ODBCドライバはAdministratorで定義されている値を読み取りません。

DRIVER={Oracle ODBC Driver}

有効な接続文字列の例:

1) DSN=Personnel;UID=Kotzwinkle;PWD=;2) DRIVER={Oracle ODBC Driver};UID=Kotzwinkle;PWD=whatever;DBQ=instl_alias;DBA=W;

関連項目:

5.2 SQLDriverConnectの実装

次の表に、SQLDriverConnect接続文字列で必要なキーワードを示します。

表5-2 SQLDriverConnect接続文字列で必要なキーワード

キーワード 説明

DSN

データソース名。

DBQ

TNSサービス名。「Oracle Database ODBCドライバのTNSサービス名の作成」を参照してください。詳細は、Oracle Net Servicesのマニュアルを参照してください。

UID

ユーザーのログインIDまたはユーザー名。

PWD

ユーザー指定のパスワード。

5.3 プログラムでのロック・タイムアウトの削減

Oracleサーバーは、トランザクション間のロック競合が解消されるまで無期限に待機します。データソースに接続する前にODBC SQLSetConnectAttr関数をコールすることによって、ロック解消をOracleサーバーが待機する時間の長さを制限できます。

ODBC SQLSetStmtAttr関数のSQL_ATTR_QUERY_TIMEOUT属性に0 (ゼロ)以外の値を指定します。ODBC SQLSetConnectAttr関数を使用してロック・タイムアウト値を指定すると、その値によってoraodbc.iniファイルに指定されている値が上書きされます。

関連項目:

oraodbc.iniファイルに値を指定する方法の詳細は、「ロック・タイムアウトの削減」を参照してください

5.4 odbc32.lib (Windows)またはlibodbc.so (UNIX)とのリンク

Windowsプラットフォームの場合、プログラムをリンクするときは、インポート・ライブラリodbc32.libとリンクする必要があります。

UNIXプラットフォームの場合は、ODBCアプリケーションをlibodbc.soにリンクする必要があります。

5.5 ROWIDに関する情報

ODBCのSQLSpecialColumns関数は、表内の列に関する情報を返します。Oracle Database ODBCドライバでは、Oracle表に関連付けられているOracle ROWIDに関する情報を返します。

5.6 WHERE句のROWID

ROWIDはSQL文のWHERE句で使用できます。ただし、ROWID値はパラメータ・マーカー内に存在している必要があります。

5.7 結果セットの有効化

Oracleの参照カーソル(結果セット)によって、アプリケーションはストアド・ファンクションやストアド・プロシージャを使用してデータを取り出せるようになります。次にODBCを介して結果セットを使用可能にする方法を示します。

  • ストアド・プロシージャのコールにはODBC構文を使用する必要があります。システム固有のPL/SQLは、ODBCを介してサポートされていません。次にパッケージ内のおよびパッケージなしのプロシージャまたはファンクションのコール方法を示します。ここではパッケージ名をRSETとします。

    Procedure call: 
    {CALL Example1(?)}
    {CALL RSET.Example1(?)} 
    Function Call: 
    {? = CALL Example1(?)}
    {? = CALL RSET.Example1(?)} 
    
  • PL/SQLの参照カーソル・パラメータは、プロシージャのコール時には省略されます。たとえば、プロシージャExample2には4つのパラメータが定義されているとします。パラメータ1と3は参照カーソル・パラメータで、パラメータ2と4は文字列です。コールは、次のように指定されます。

    {CALL RSET.Example2("Literal 1", "Literal 2")}
    

次の例のアプリケーションは、Oracle Database ODBCドライバを使用して結果セットを返す方法を示しています。

/*
 * Sample Application using Oracle reference cursors via ODBC
 *
 * Assumptions:
 *
 * 1) Oracle Sample database is present with data loaded for the EMP table.
 * 2) Two fields are referenced from the EMP table ename and mgr.
 * 3) A data source has been setup to access the sample database.
 *
 * Program Description:
 *
 * Abstract:
 *
 * This program demonstrates how to return result sets using
 * Oracle stored procedures
 *
 * Details:
 *
 * This program:
 * Creates an ODBC connection to the database.
 * Creates a Packaged Procedure containing two result sets.
 * Executes the procedure and retrieves the data from both result sets.
 * Displays the data to the user.
 * Deletes the package then logs the user out of the database.
 *
 *
 * The following is the actual PL/SQL this code generates to
 * create the stored procedures.
 *
 * DROP PACKAGE ODBCRefCur;
 *
 * CREATE PACKAGE ODBCRefCur AS
 * TYPE ename_cur IS REF CURSOR;
 * TYPE mgr_cur IS REF CURSOR;
 * PROCEDURE EmpCurs(Ename IN OUT ename_cur, Mgr IN OUT mgr_cur, pjob IN VARCHAR2);
 * END;
 *
 * CREATE or REPLACE PACKAGE BODY ODBCRefCur AS
 * PROCEDURE EmpCurs(Ename IN OUT ename_cur, Mgr IN OUT mgr_cur, pjob IN VARCHAR2)
 * AS
 * BEGIN
 * IF NOT Ename%ISOPEN
 * THEN
 * OPEN Ename for SELECT ename from emp;
 * END IF;
 * IF NOT Mgr%ISOPEN
 * THEN
 * OPEN Mgr for SELECT mgr from emp where job = pjob;
 * END IF;
 * END;
 * END;
 *
 */

/* Include Files */
#ifdef WIN32
  #include <windows.h>
#endif
#include <stdio.h>
#include <sql.h>
#include <sqlext.h>
 
/* Defines */
#define JOB_LEN 9
#define DATA_LEN 100
#define SQL_STMT_LEN 500
 
/* Procedures */
void DisplayError(SWORD HandleType, SQLHANDLE hHandle, char *Module);
 
/* Main Program */
int main()
{
  SQLHENV hEnv;
  SQLHDBC hDbc;
  SQLHSTMT hStmt;
  SQLRETURN rc;
  char *DefUserName ="scott";
  char *DefPassWord ="tiger";
  SQLCHAR ServerName[DATA_LEN];
  SQLCHAR *pServerName=ServerName;
  SQLCHAR UserName[DATA_LEN];
  SQLCHAR *pUserName=UserName;
  SQLCHAR PassWord[DATA_LEN];
  SQLCHAR *pPassWord=PassWord;
  char Data[DATA_LEN];
  SQLINTEGER DataLen;
  char error[DATA_LEN];
  char *charptr;
  SQLCHAR SqlStmt[SQL_STMT_LEN];
  SQLCHAR *pSqlStmt=SqlStmt;
  char *pSalesMan = "SALESMAN";
  SQLINTEGER sqlnts=SQL_NTS;

  /* Allocate the Environment Handle */
  rc = SQLAllocHandle( SQL_HANDLE_ENV, SQL_NULL_HANDLE, &hEnv );
  if (rc != SQL_SUCCESS)
  {
    printf( "Cannot Allocate Environment Handle/n");
    printf( "/nHit Return to Exit/n");
    charptr = gets ((char *)error);
    exit(1);
  }

  /* Set the ODBC Version */
  rc = SQLSetEnvAttr(hEnv, SQL_ATTR_ODBC_VERSION, (void *)SQL_OV_ODBC3, 0);
  if (rc != SQL_SUCCESS)
  {
    printf("Cannot Set ODBC Version/n");
    printf("/nHit Return to Exit/n");
    charptr = gets((char *)error);
    exit(1);
  }


  /* Allocate the Connection handle */
  rc = SQLAllocHandle(SQL_HANDLE_DBC, hEnv, &hDbc);
  if (rc != SQL_SUCCESS)
  {
    printf("Cannot Allocate Connection Handle/n");
    printf("/nHit Return to Exit/n");
    charptr = gets((char*) error);
    exit(1);
  }

  /* Get User Information */
  lstrcpy((char*) pUserName, DefUserName);
  lstrcpy((char*) pPassWord, DefPassWord);

  /* Data Source name */
  printf( "/nEnter the ODBC Data Source Name/n" );
  charptr = gets((char*) ServerName);

  /* User Name */
  printf("/nEnter User Name Default [%s]/n", pUserName);
  charptr = gets((char*) UserName);
  if (*charptr == '/0')
  {
    lstrcpy((char*) pUserName, (char*) DefUserName);
  }

  /* Password */
  printf ("/nEnter Password Default [%s]/n", pPassWord);
  charptr = gets((char*) PassWord);
  if (*charptr == '/0')
  {
    lstrcpy((char*) pPassWord, (char*) DefPassWord);
  }

  /* Connection to the database */
  rc = SQLConnect(hDbc, pServerName, (SQLSMALLINT) lstrlen((char *)pServerName), pUserName,
                  (SQLSMALLINT) lstrlen((char*)pUserName), pPassWord,
                  (SQLSMALLINT) lstrlen((char *)pPassWord));
  if (rc != SQL_SUCCESS)
  {
    DisplayError(SQL_HANDLE_DBC, hDbc, "SQLConnect");
  }
 
  /* Allocate a Statement */
  rc = SQLAllocHandle(SQL_HANDLE_STMT, hDbc, &hStmt);
  if (rc != SQL_SUCCESS)
  {
    printf( "Cannot Allocate Statement Handle/n");
    printf( "/nHit Return to Exit/n");
    charptr = gets((char *)error);
    exit(1);
  }

  /* Drop the Package */
  lstrcpy((char *) pSqlStmt, "DROP PACKAGE ODBCRefCur");
  rc = SQLExecDirect(hStmt, pSqlStmt, lstrlen((char *)pSqlStmt));

  /* Create the Package Header */
  lstrcpy((char *) pSqlStmt, "CREATE PACKAGE ODBCRefCur AS/n" );
  lstrcat((char *) pSqlStmt, " TYPE ename_cur IS REF CURSOR;/n" );
  lstrcat((char *) pSqlStmt, " TYPE mgr_cur IS REF CURSOR;/n" );
  lstrcat((char *) pSqlStmt, " PROCEDURE EmpCurs (Ename IN OUT ename_cur," );
  lstrcat((char *) pSqlStmt, " Mgr IN OUT mgr_cur,pjob IN VARCHAR2);/n/n");
  lstrcat((char *) pSqlStmt, "END;/n" );

  rc = SQLExecDirect(hStmt, pSqlStmt, lstrlen((char *)pSqlStmt));
  if (rc != SQL_SUCCESS)
  {
    DisplayError(SQL_HANDLE_STMT, hStmt, "SQLExecDirect");
  }

  /* Create the Package Body */
  lstrcpy((char *) pSqlStmt, "CREATE PACKAGE BODY ODBCRefCur AS/n" );
  lstrcat((char *) pSqlStmt, " PROCEDURE EmpCurs (Ename IN OUT ename_cur," );
  lstrcat((char *) pSqlStmt, " Mgr IN OUT mgr_cur, pjob IN VARCHAR2)/n" );
  lstrcat((char *) pSqlStmt, " AS/n" );
  lstrcat((char *) pSqlStmt, " BEGIN/n" );
  lstrcat((char *) pSqlStmt, " IF NOT Ename%ISOPEN/n" );
  lstrcat((char *) pSqlStmt, " THEN/n" );
  lstrcat((char *) pSqlStmt, " OPEN Ename for SELECT ename from emp;/n" );
  lstrcat((char *) pSqlStmt, " END IF;/n/n" );
  lstrcat((char *) pSqlStmt, " IF NOT Mgr%ISOPEN/n THEN/n" );
  lstrcat((char *) pSqlStmt, " OPEN Mgr for SELECT mgr from emp where job = pjob;/n");
  lstrcat((char *) pSqlStmt, " END IF;/n" );
  lstrcat((char *) pSqlStmt, " END;/n" );


  lstrcat((char *) pSqlStmt, "END;/n" );

  rc = SQLExecDirect(hStmt, pSqlStmt, lstrlen((char *)pSqlStmt));
  if(rc != SQL_SUCCESS)
    DisplayError(SQL_HANDLE_STMT, hStmt, "SQLExecDirect");

  /* Bind the Parameter */
  rc = SQLBindParameter(hStmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, JOB_LEN, 0, pSalesMan, 0, &sqlnts);

  /* Call the Store Procedure which executes the Result Sets */
  lstrcpy( (char *) pSqlStmt, "{CALL ODBCRefCur.EmpCurs(?)}");

  rc = SQLExecDirect(hStmt, pSqlStmt, lstrlen((char *)pSqlStmt));
  if(rc != SQL_SUCCESS)
    DisplayError(SQL_HANDLE_STMT, hStmt, "SQLExecDirect");

  /* Bind the Data */
  rc = SQLBindCol(hStmt, 1, SQL_C_CHAR, Data, sizeof(Data), &DataLen);
  if(rc != SQL_SUCCESS)
    DisplayError(SQL_HANDLE_STMT, hStmt, "SQLBindCol");
 
  /* Get the data for Result Set 1 */
  printf("/nEmployee Names/n/n");

  while(rc == SQL_SUCCESS)
  {
    rc = SQLFetch(hStmt);
    if(rc == SQL_SUCCESS)
      printf("%s/n", Data);
    else
      if(rc != SQL_NO_DATA)
        DisplayError(SQL_HANDLE_STMT, hStmt, "SQLFetch");
  }

  printf( "/nFirst Result Set - Hit Return to Continue/n");
  charptr = gets ((char *)error);

  /* Get the Next Result Set */
  rc = SQLMoreResults( hStmt );
  if(rc != SQL_SUCCESS)
    DisplayError(SQL_HANDLE_STMT, hStmt, "SQLMoreResults");

  /* Get the data for Result Set 2 */
  printf("/nManagers/n/n");
  while (rc == SQL_SUCCESS)
  {
    rc = SQLFetch(hStmt);
    if(rc == SQL_SUCCESS)
      printf("%s/n", Data);
    else
      if (rc != SQL_NO_DATA)
        DisplayError(SQL_HANDLE_STMT, hStmt, "SQLFetch");
  }

  printf("/nSecond Result Set - Hit Return to Continue/n");
  charptr = gets((char *)error);

  /* Should Be No More Results Sets */
  rc = SQLMoreResults( hStmt );
  if (rc != SQL_NO_DATA)
    DisplayError(SQL_HANDLE_STMT, hStmt, "SQLMoreResults");

  /* Drop the Package */
  lstrcpy((char *)pSqlStmt, "DROP PACKAGE ODBCRefCur");
  rc = SQLExecDirect(hStmt, pSqlStmt, lstrlen((char *)pSqlStmt));

  /* Free handles close connections to the database */
  SQLFreeHandle( SQL_HANDLE_STMT, hStmt );
  SQLDisconnect( hDbc );
  SQLFreeHandle( SQL_HANDLE_DBC, hDbc );
  SQLFreeHandle( SQL_HANDLE_ENV, hEnv );

  printf( "/nAll Done - Hit Return to Exit/n");
  charptr = gets ((char *)error);
  return(0);
}
 
/* Display Error Messages */
void DisplayError( SWORD HandleType, SQLHANDLE hHandle, char *Module )
{
  SQLCHAR MessageText[255];
  SQLCHAR SQLState[80];
  SQLRETURN rc=SQL_SUCCESS;
  LONG NativeError;
  SWORD RetLen;
  SQLCHAR error[25];
  char *charptr;

  rc = SQLGetDiagRec(HandleType, hHandle, 1, SQLState, &NativeError, MessageText, 255, &RetLen);
  printf( "Failure Calling %s/n", Module );
  if (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO)
  {
    printf( "/t/t/t State: %s/n", SQLState);
    printf( "/t/t/t Native Error: %d/n", NativeError );
    printf( "/t/t/t Error Message: %s/n", MessageText );
  }

  printf( "/nHit Return to Exit/n");
  charptr = gets ((char *)error);
  exit(1);
}

5.8 EXEC構文の有効化

このオプションを有効にすると、変更なしに変換可能な場合、Oracle Database ODBCドライバによって、SQL Server EXEC文の構文が対応するOracleプロシージャ・コールに変換されます。

SQL Serverプロシージャの完全な名前は、次に示す最大4つの識別子で構成されます。

  • サーバー名

  • データベース名

  • 所有者名

  • プロシージャ名

この名前の書式は次のとおりです。

[[[server.][database].][owner_name].]procedure_name

SQL ServerデータベースからOracleへの移行時、それぞれのSQL Serverプロシージャ(またはファンクション)定義は、対応するOracle構文に変換され、Oracleスキーマに定義されます。移行したプロシージャは、多くの場合、次のいずれかの方法で再編成(およびスキーマで作成)されます。

  • すべてのプロシージャが1つのスキーマに移行されます(デフォルト・オプション)。

  • 1つのSQL Serverデータベースに定義されているすべてのプロシージャが、そのデータベース名の付いたスキーマに移行されます。

  • 特定のユーザーが所有するすべてのプロシージャが、そのユーザー名の付いたスキーマに移行されます。

移行したプロシージャを編成するこれら3通りの方法をサポートするためには、いずれかのスキーマ名オプションを指定してプロシージャ名を変換できます。変換したOracleプロシージャ・コールのオブジェクト名では、大/小文字が区別されません。

5.9 Oracle RAC環境における接続障害のイベント通知の有効化

Oracle Real Application Clusters (Oracle RAC)データベース環境で接続障害が発生したときに、SQLSetConnectAttr関数のSQL_ORCLATTR_FAILOVER_CALLBACKおよびSQL_ORCLATTR_FAILOVER_HANDLE属性が設定されている場合、イベント通知が有効になります。どちらの属性も、SQLSetConnectAttr関数を使用して設定されます。新規属性のシンボルは、ファイルsqora.hで定義されています。

SQL_ORCLATTR_FAILOVER_CALLBACK属性は、障害イベントが発生したときにコールするルーチンのアドレスを指定します。

SQL_ORCLATTR_FAILOVER_HANDLE属性は、コールバック・ルーチンのパラメータとして渡されるコンテキスト・ハンドルを指定します。この属性は、ODBCアプリケーションで障害イベントが発生している接続を判別するために必要です。

コールバック・ルーチンの関数プロトタイプは次のとおりです。

void failover_callback(void *handle, SQLINTEGER fo_code)

'handle'パラメータは、SQL_ORCLATTR_FAILOVER_HANDLE属性により設定された値です。この属性が設定されていない場合、NULLが戻されます。

fo_codeパラメータにより、発生している障害イベントが識別されます。障害イベントは、OCIプログラミング・インタフェースで定義されているイベントに直接マップされます。発生する可能性があるイベントのリストを次に示します。

  • ODBC_FO_BEGIN

  • ODBC_FO_ERROR

  • ODBC_FO_ABORT

  • ODBC_FO_REAUTH

  • ODBC_FO_END

この機能の使用方法を示したサンプル・プログラムは次のとおりです。

/*
  NAME
  ODBCCallbackTest
 
  DESCRIPTION
  Simple program to demonstrate the connection failover callback feature.
 
  PUBLIC FUNCTION(S)
  main
 
  PRIVATE FUNCTION(S)
 
  NOTES
 
  Command Line: ODBCCallbackTest filename [odbc-driver]
 
*/
 
#include <windows.h>
#include <tchar.h>
#include <malloc.h>
#include <stdio.h>
#include <string.h>
#include <sql.h>
#include <sqlext.h>
#include "sqora.h"
 
/*
** Function Prototypes
*/
void display_errors(SQLSMALLINT HandleType, SQLHANDLE Handle);
void failover_callback(void *Handle, SQLINTEGER fo_code);
 
/*
** Macros
*/
#define ODBC_STS_CHECK(sts) \
  if (sts != SQL_SUCCESS) \
{ \
  display_errors(SQL_HANDLE_ENV, hEnv); \
  display_errors(SQL_HANDLE_DBC, hDbc); \
  display_errors(SQL_HANDLE_STMT, hStmt); \
  return FALSE; \
}
 
/*
** ODBC Handles
*/
SQLHENV *hEnv = NULL; // ODBC Environment Handle
SQLHANDLE *hDbc = NULL; // ODBC Connection Handle
SQLHANDLE *hStmt = NULL; // ODBC Statement Handle
 
/*
** Connection Information
*/
TCHAR *dsn = _T("odbctest");
TCHAR *uid = _T("scott");
TCHAR *pwd = _T("tiger");
TCHAR *szSelect = _T("select * from emp");
 
/*
** MAIN Routine
*/
main(int argc, char **argv)
{
  SQLRETURN rc;
 
  /*
  ** Allocate handles
  */
  rc = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, (SQLHANDLE *)&hEnv);
  ODBC_STS_CHECK(rc)
 
  rc = SQLSetEnvAttr(hEnv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3, 0);
  ODBC_STS_CHECK(rc);
 
  rc = SQLAllocHandle(SQL_HANDLE_DBC, hEnv, (SQLHANDLE *)&hDbc);
  ODBC_STS_CHECK(rc);
 
  /*
  ** Connect to the database
  */
  rc = SQLConnect(hDbc, dsn, (SQLSMALLINT)_tcslen(dsn),
  uid, (SQLSMALLINT)_tcslen(uid),
  pwd, (SQLSMALLINT)_tcslen(pwd));
  ODBC_STS_CHECK(rc);
 
  /*
  ** Set the connection failover attributes
  */
  rc = SQLSetConnectAttr(hDbc, SQL_ORCLATTR_FAILOVER_CALLBACK, &failover_callback, 0);
  ODBC_STS_CHECK(rc);
 
  rc = SQLSetConnectAttr(hDbc, SQL_ORCLATTR_FAILOVER_HANDLE, hDbc, 0);
  ODBC_STS_CHECK(rc);
 
  /*
  ** Allocate the statement handle
  */
  rc = SQLAllocHandle(SQL_HANDLE_STMT, hDbc, (SQLHANDLE *)&hStmt);
  ODBC_STS_CHECK(rc);
 
  /*
  ** Wait for connection failovers
  */
   while (TRUE)
  {
   Sleep(5000);
   rc = SQLExecDirect(hStmt,szSelect, _tcslen(szSelect));
   ODBC_STS_CHECK(rc);
 
   rc = SQLFreeStmt(hStmt, SQL_CLOSE);
   ODBC_STS_CHECK(rc);
  }
 
  /*
  ** Free up the handles and close the connection
  */
  rc = SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
  ODBC_STS_CHECK(rc);
 
  rc = SQLDisconnect(hDbc);
  ODBC_STS_CHECK(rc);
 
  rc = SQLFreeHandle(SQL_HANDLE_DBC, hDbc);
  ODBC_STS_CHECK(rc);
 
  rc = SQLFreeHandle(SQL_HANDLE_ENV, hEnv);
  ODBC_STS_CHECK(rc);
 
  return TRUE;
}
 
/*
** Failover Callback Routine
*/
void failover_callback(void *Handle, SQLINTEGER fo_code)
{
  switch(fo_code)
  {
  case ODBC_FO_BEGIN:
    printf("ODBC_FO_BEGIN recevied\n");
    break;
   
  case ODBC_FO_ERROR:
    printf("ODBC_FO_ERROR recevied\n");
    break;
  
  case ODBC_FO_ABORT:
    printf("ODBC_FO_ABORT recevied\n");
    break;
  
  case ODBC_FO_REAUTH:
    printf("ODBC_FO_REAUTH recevied\n");
    break;
  
  case ODBC_FO_END:
    printf("ODBC_FO_END recevied\n");
    break;
  
  default:
    printf("Invalid or unknown ODBC failover code recevied\n");
    break;
  } 
  return;
 
}
 
/*
** Retrieve the errors associated with the handle passed
** and display them.
*/
void display_errors(SQLSMALLINT HandleType, SQLHANDLE Handle)
{
  SQLTCHAR MessageText[256];
  SQLTCHAR SqlState[5+1];
  SQLSMALLINT i=1;
  SQLINTEGER NativeError;
  SQLSMALLINT TextLength;
  SQLRETURN sts = SQL_SUCCESS;
 
  if (Handle == NULL) return;
 
  /* Make sure all SQLState text is null terminated */
  SqlState[5] = '\0';
 
  /*
  ** Fetch and display all diagnostic records that exist for this handle
  */
  while (sts == SQL_SUCCESS)
  {
    NativeError = 0;
    TextLength = 0;

    sts = SQLGetDiagRec(HandleType, Handle, i, SqlState, &NativeError, (SQLTCHAR *)&MessageText, sizeof(MessageText), &TextLength);
    if (sts == SQL_SUCCESS)
    {
      printf("[%s]%s\n", SqlState, MessageText);
      if (NativeError != 0)
        printf("Native Error Code: %d\n", NativeError);
      i++;
    }
  } 
  return;
}

5.10 ODBCを通じた暗黙的結果機能の使用

このオプションは、サード・パーティODBCアプリケーションをOracleデータベースに移行し、前のベンダーでサポートされている暗黙的結果機能を使用する場合に使用します。Oracle Database ODBCドライバでは、ストアド・プロシージャまたは匿名PL/SQLブロックでの暗黙的結果がサポートされます。現在のリリースでは、暗黙的結果はSELECT文でのみ戻されます。

次のコード例は、暗黙的結果に匿名SQLスクリプトを使用したODBCテスト・ケースの例を示しています。

const char *query1="declare \
                  c1 sys_refcursor; \
                  c2 sys_refcursor; \
                  begin \
                  open c1 for select empno,ename from emp where rownum<=3; \
                  dbms_sql.return_result(c1); \
                  open c2 for select empno,ename from emp where rownum<=3; \
                  dbms_sql.return_result(c2); end; ";
 
int main( )
{
  ...
   ...
 //Allocate all required handles and establish a connection to the database.
 
 //Prepare and execute the above anonymous PL/SQL block
    SQLPrepare (hstmt, (SQLCHAR *) query1, SQL_NTS);
    SQLExecute(hstmt);
 
 //Bind the columns for the results from the first SELECT statement in an anonymous block.
    SQLBindCol (hstmt, 1, SQL_C_ULONG, &eno, 0, &jind);
    SQLBindCol (hstmt, 2, SQL_C_CHAR, empname, sizeof (empname),&enind);
 
 //Fetch implicit results through the SQLFetch( ) call.
    while((retCode = SQLFetch(hstmt)) != SQL_NO_DATA)
    {
 //Do whatever you want to do with the data.
    }
 
     retCode = SQLMoreResults(hstmt);
 
     if(retCode == SQL_SUCCESS)
    {
      printf("SQLMoreResults returned with SQL_SUCCESS\n");
 
 //Bind the columns for the results from the second SELECT statement in an anonymous block.
    SQLBindCol (hstmt, 1, SQL_C_ULONG, &eno, 0, &jind);
    SQLBindCol (hstmt, 2, SQL_C_CHAR, empname, sizeof (empname),&enind);
 
 //Fetch implicit results through the SQLFetch( ) call.
    while((retCode = SQLFetch(hstmt)) != SQL_NO_DATA)
    {
 //Do whatever you want to do with data.
    }
  }
}

5.11 ODBCでのOracle TIMESTAMP WITH TIME ZONEおよびTIMESTAMP WITH LOCAL TIME ZONE列タイプのサポートについて

タイムゾーンはシステム変数ORA_SDTZに定められています。システム変数は、'OS_TZ''DB_TZ'または有効なタイムゾーン値に設定できます。ORA_SDTZ'OS_TZ'に設定すると、オペレーティング・システムのタイムゾーンが使用されます。'DB_TZ'に設定した場合は、データベースに設定されたデフォルトのタイムゾーンが使用されます。

ORA_SDTZが設定されていない場合、デフォルトではオペレーティング・システムのタイムゾーンが使用されます。

ノート:

Microsoft Windows環境のレジストリ、システム環境変数またはコマンド・プロンプト・ウィンドウでORA_SDTZ変数を設定する場合、タイム・ゾーン値を引用符で囲まないでください。

関連項目:

日時データ型およびタイム・ゾーン・サポートの詳細は、『Oracle Databaseグローバリゼーション・サポート・ガイド』を参照してください

ODBCデータ型TIMESTAMP_STRUCTの変数を使用したこれらのタイムゾーン列からのデータのフェッチ

次の例では、ODBCデータ型TIMESTAMP_STRUCTの変数を使用して、TIMESTAMP WITH TIME ZONE列およびTIMESTAMP WITH LOCAL TIME ZONE列からデータをフェッチする方法を示します。

例5-1 ODBCデータ型TIMESTAMP_STRUCTの変数を使用してTIMESTAMP WITH TIME ZONE列およびTIMESTAMP WITH LOCAL TIME ZONE列からデータをフェッチする方法

int main() 
{ 
... 
...  
 /* TSTAB table's DDL statement:  
  * ---------------------------  
  * CREATE TABLE TSTAB (COL_TSTZ  TIMESTAMP WITH TIME ZONE,  
  *                     COL_TSLTZ TIMESTAMP WITH LOCAL TIME ZONE);  
  *  
  * Insert statement:  
  * ----------------  
  * Sample #1:  
  * ---------  
  * INSERT INTO TSTAB VALUES (TIMESTAMP '2010-03-13 03:47:30.123456 America/Los_Angeles'  
  *                           TIMESTAMP '2010-04-14 04:47:30.123456 America/Los_Angeles');  
  *  
  * Sample #2:  
  * ---------  
  * INSERT INTO TSTAB VALUES ('22-NOV-1963 12:30:00.000000 PM',  
  *                           '24-NOV-1974 02:30:00.000000 PM');  
  *  
  * Refer Oracle Database documentations to know more details about TIMESTAMP  
  * WITH TIME ZONE and TIMESTAMP WITH LOCAL TIME ZONE columns.  
  */   
 SQLCHAR sqlSelQuery[] = "SELECT COL_TSTZ, COL_TSLTZ FROM TSTAB";   
 TIMESTAMP_STRUCT timestampcol1;   
 TIMESTAMP_STRUCT timestampcol2; 
... 
...  
  /* Allocate the ODBC statement handle. */ 
  SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);  

  /* Execute the statement sqlSelQuery. */ 
  SQLExecDirect(hstmt, sqlSelQuery, SQL_NTS); 
 
  /* Bind the variable to read the value from the TIMESTAMP WITH TIME ZONE column. */ 
  SQLBindCol(hstmt, 1, SQL_C_TIMESTAMP, &timestampcol1, sizeof(timestampcol1), NULL); 
 
  /* Bind the variable to read the value from the TIMESTAMP WITH LOCAL TIME ZONE column. */ 
  SQLBindCol(hstmt, 2, SQL_C_TIMESTAMP, &timestampcol2, sizeof(timestampcol2), NULL); 
... 
... 
  /* Fetch data from the TSTAB table. */ 
  retcode = SQLFetch(hstmt);  
  /* Values of column COL_TSTZ and COL_TSLTZ are available in variables  
  * timestampcol1 and timestampcol2 respectively. Refer to Microsoft ODBC  
  * documentation for more information about data type TIMESTAMP_STRUCT. */ 

... 
... 
  /* Close the statement. */ 
  SQLFreeStmt(hstmt, SQL_CLOSE); 
  /* Free the statement handle. */ 
  SQLFreeHandle(SQL_HANDLE_STMT, hstmt); ... ... }

例5-2 TIMESTAMP WITH TIME ZONE列およびTIMESTAMP WITH LOCAL TIME ZONE列にデータを挿入する方法

int main() 
{ 
... 
...   
 SQLCHAR sqlInsQuery[]   = "INSERT INTO TSTAB VALUES (?, ?)";   
 TIMESTAMP_STRUCT timestampcol1;   
 TIMESTAMP_STRUCT timestampcol2; 
... 
...  
 /* Input the value for column COL_TSTZ in table TSTAB. */ 
  timestampcol1.year = 2000; 
  timestampcol1.month = 1; 
  timestampcol1.day   = 1; 
  timestampcol1.hour  = 0; 
  timestampcol1.minute = 0;
  timestampcol1.second = 1; 
  timestampcol1.fraction = 1000; 
 
 /* Input the value for column COL_TSLTZ in table TSTAB. */ 
  timestampcol1.year = 2012; 
  timestampcol1.month = 2; 
  timestampcol1.day   = 5; 
  timestampcol1.hour  = 10; 
  timestampcol1.minute = 30; 
  timestampcol1.second = 10; 
  timestampcol1.fraction = 1000; 
... 
... 
  /* Allocate the ODBC statement handle. */ 
  SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt); 
... 
... 
  /* Bind the input value for column COL_TSTZ. */ 
  SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_TIMESTAMP, SQL_TIMESTAMP,   
    0, 0, &timestampcol1, sizeof(timestampcol1), NULL);
  
  /* Bind the input value for column COL_TSLTZ. */ 
  SQLBindParameter(hstmt, 2, SQL_PARAM_INPUT, SQL_C_TIMESTAMP, SQL_TIMESTAMP,  
    0, 0, &timestampcol2, sizeof(timestampcol2), NULL); 
... 
... 
  /* Execute the statement sqlInsQuery. */ 
  SQLExecDirect(hstmt, sqlInsQuery, SQL_NTS);
  
  /* Close the statement. */ 
  SQLFreeStmt(hstmt, SQL_CLOSE); 
  /* Free the statement handle. */ 
  SQLFreeHandle(SQL_HANDLE_STMT, hstmt); 
... 
... 
}

5.12 Oracleクライアント(OCI、SQL*Plus、Oracle Database ODBCドライバなど)でORA_SDTZを設定した場合の影響について

Oracleクライアントでシステム変数ORA_SDTZを設定した場合の影響について説明します。

タイムゾーンはシステム変数ORA_SDTZに定められています。

次の項では、Oracleクライアント(OCI、SQL*Plus、Oracle Database ODBCドライバなど)でシステム変数ORA_SDTZを設定した場合や設定しなかった場合の影響について説明します。これらの項の例は、インド(GMT+5:30)のタイムゾーンで実行されています。

関連項目:

セッション・タイム・ゾーンの設定の詳細は、『Oracle Databaseグローバリゼーション・サポート・ガイド』を参照してください

環境設定

環境を設定するには、TSLTZ (TIMESTAMP WITH LOCAL TIME ZONE)列のある次の表を作成し、01/01/2016 00:00 GMTの値を次に示すようにTSLTZ列に挿入します。

例5-3 環境の設定方法

次の例では、例を示す後続の項の環境を設定しています。

SQL> create table timezone_demo(col1 TIMESTAMP WITH LOCAL TIME ZONE);

Table created.

SQL> INSERT INTO TIMEZONE_DEMO VALUES(TIMESTAMP '2016-01-01 00:00:00.000000 ETC/GREENWICH');

1 row created.

環境内でORA_SDTZが設定されない場合

環境内でORA_SDTZが設定されない場合は、オペレーティング・システム(OS)のタイムゾーン設定がOracleクライアントのデフォルトのタイムゾーンとして使用されます。次に例を示します。

例5-4 ORA_SDTZが設定されない場合

C:\Users\example.ORADEV>set ORA_SDTZ=

C:\Users\example.ORADEV>sqlplus scott/password@//host01.example.com:1521/ORCL12C1

SQL*Plus: Release 23.0.0.0.0 - Production on Wed Jun 19 13:14:27 2024 Version 23.4.1.24.05

Copyright (c) 1982, 2024, Oracle.  All rights reserved.

Connected to:Oracle Database 23ai Enterprise Edition Release 23.0.0.0.0 - Development Version 23.4.0.24.00
With the Partitioning, OLAP, Advanced Analytics, and Real Application Testing options

SQL> select sessiontimezone from dual;

SESSIONTIMEZONE
---------------------------------------------------------------------------
+05:30

SQL> select * from timezone_demo;

COL1
---------------------------------------------------------------------------
01-JAN-24 05.30.00.000000 AM

環境内でORA_SDTZをオペレーティング・システム(OS)のタイムゾーンに設定する場合

ORA_SDTZがオペレーティング・システム(OS)・タイム・ゾーンに設定されている場合、Oracleクライアントのユーザー・セッションはOSタイム・ゾーン設定に設定されます。環境で設定を解除するか、ORA_SDTZOS_TZに設定できます。次に例を示します。

例5-5 ORA_SDTZがオペレーティング・システム(OS)のタイムゾーンに設定される場合

C:\Users\example.ORADEV>set ORA_SDTZ=OS_TZ

C:\Users\example.ORADEV>sqlplus scott/password@//host01.example.com:1521/ORCL12C1
SQL*Plus: Release 23.0.0.0.0 - for Oracle Cloud on Wed Jun 19 13:14:27 2024 Version 23.4.1.24.06

Copyright (c) 1982, 2024, Oracle.  All rights reserved.

Connected to:Oracle Database 23ai Enterprise Edition Release 23.0.0.0.0 - for Oracle Cloud Version 23.4.1.24.06
With the Partitioning, OLAP, Advanced Analytics, and Real Application Testing options

SQL> select sessiontimezone from dual;

SESSIONTIMEZONE
---------------------------------------------------------------------------
+05:30

SQL> select * from timezone_demo;

COL1
---------------------------------------------------------------------------
01-JAN-24 05.30.00.000000 AM

環境内でORA_SDTZを特定のタイムゾーンに設定する場合

Oracleクライアントは、特定のタイム・ゾーン(たとえば、ヘルシンキ・タイムゾーン)に調整されたタイム・スタンプ値を取得するように設定できます。これを行うには、ORA_SDTZを、対応するタイムゾーンのOracleタイムゾーンのリージョン名(ヘルシンキのタイムゾーンのOracleタイムゾーンのリージョン名はEurope/Helsinki)に設定します。次に例を示します。

例5-6 ORA_SDTZが特定のタイムゾーンに設定される場合

C:\Users\example.ORADEV>set ORA_SDTZ=Europe/Helsinki

C:\Users\example.ORADEV>sqlplus scott/password@//host01.example.com:1521/ORCL12C1

SQL*Plus: Release 23.0.0.0.0 - for Oracle Cloud on Wed Jun 19 13:14:27 2024 Version 23.4.1.24.06

Copyright (c) 1982, 2024, Oracle.  All rights reserved.

Connected to:Oracle Database 23ai Enterprise Edition Release 23.0.0.0.0 - for Oracle Cloud Version 23.4.1.24.06
With the Partitioning, OLAP, Advanced Analytics, and Real Application Testing options

SQL> select sessiontimezone from dual;

SESSIONTIMEZONE
---------------------------------------------------------------------------
Europe/Helsinki

SQL> select * from timezone_demo;

COL1
---------------------------------------------------------------------------
01-JAN-24 02.00.00.000000 AM