5 Oracle Database ODBC Driver for Programmers

The chapter is intended for programmers who want to develop robust ODBC applications using the Oracle Database ODBC driver.

Topics:

5.1 Format of the Connection String

The following table describes keywords that can be included in the connection string argument of the SQLDriverConnect function call. Missing keywords are read from the Administrator entry for the data source. Values specified in the connection string override those contained in the Administrator entry. See the Microsoft ODBC 3.52 Software Development Kit and Programmer's Reference for more information about the SQLDriverConnect function.

Table 5-1 Keywords that Can Be Included in the Connection String Argument of the SQLDriverConnect Function Call

Keyword Meaning Comments

DSN

ODBC Data Source Name

User-supplied name.

DBQ

TNS Service Name

User-supplied name.

UID

User ID or User Name

User-supplied name.

PWD

Password

User-supplied password. Specify PWD=; for an empty password.

DBA

Database Attribute

W=write access.

R=read-only access.

APA

Applications Attributes

T=Thread Safety Enabled.

F=Thread Safety Disabled.

RST

Result Sets

T=Result Sets Enabled.

F=Result Sets Disabled.

QTO

Query Timeout Option

T=Query Timeout Enabled.

F=Query Timeout Disabled.

CSR

Close Cursor

T=Close Cursor Enabled.

F=Close Cursor Disabled.

BNF

Bind NUMBER as FLOAT

T=Bind NUMBER as FLOAT.

F=Bind NUMBER as NUMBER.

DRH

Disable Rule Hint

T=Disable Rule Hint.

F=Enable Rule Hint.

BAM

Batch Autocommit Mode

IfAllSuccessful=Commit only if all statements are successful (old behavior).

UpToFirstFailure=Commit up to first failing statement (V7 ODBC behavior).

AllSuccessful=Commit all successful statements (only when connected to an Oracle database; against other databases, same behavior as V7).

FBS

Fetch Buffer Size

User-supplied numeric value (specify a value in bytes of 0 or greater).

The default is 60,000 bytes.

FEN

Failover

T=Failover Enabled.

F=Failover Disabled.

FRC

Failover Retry Count

User-supplied numeric value.

The default is 10.

FDL

Failover Delay

User-supplied numeric value.

The default is 10.

LOB

LOB Writes

T=LOBs Enabled.

F=LOBs Disabled.

MTS

Microsoft Transaction Server Support

T=Disabled.

F=Enabled.

FWC

Force SQL_WCHAR Support

T=Force SQL_WCHAR Enabled.

F=Force SQL_WCHAR Disabled.

EXC

EXEC Syntax

T=EXEC Syntax Enabled.

F=EXEC Syntax Disabled.

XSM

Schema Field

Default=Default.

Database=Database Name.

Owner=Owner Name.

MDI

Set Metadata ID Default

T=SQL_ATTR_METADATA_ID defaults to SQL_TRUE.

F=SQL_ATTR_METADATA_ID defaults to SQL_FALSE.

DPM

Disable SQLDescribeParam

T=SQLDescribeParam Disabled.

F=SQLDescribeParam Enabled.

BTD

Bind TIMESTAMP as DATE

T=Bind SQL_TIMESTAMP as Oracle DATE

F=Bind SQL_TIMESTAMP as Oracle TIMESTAMP

NUM

Numeric Settings

NLS=Use Oracle NLS numeric settings (to determine the decimal and group separator).

MS=Use Microsoft regional settings.

US=Use US settings.

ODA

Use OCIDescribeAny( )

T= Use OCIDescribeAny() call to gain performance improvement when application makes heavy calls to small packaged procedures that return REF CURSORS.

F= Do not use OCIDescribeAny(). By default, use OCIDescribeAny() value is FALSE.

STE

SQL Translate ORA Errors

Specifies whether the Oracle Database ODBC driver is to translate the Oracle error codes

T=Translate ORA errors.

F=Do not translate any ORA error. By default, SQLTranslateErrors is FALSE.

TSZ

Token Size

User-supplied numeric value.

Sets the token size to the nearest multiple of 1 KB (1024 bytes) beginning at 4 KB (4096 bytes). The default size is 8 KB (8192 bytes). The maximum value that can be set is 128 KB (131068 bytes).

If the following keyword is specified in the connection string, the Oracle Database ODBC driver does not read values defined from the Administrator:

DRIVER={Oracle ODBC Driver}

Examples of valid connection strings are:

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

5.2 SQLDriverConnect Implementation

The following table describes the keywords required by the SQLDriverConnect connection string.

Table 5-2 Keywords Required by the SQLDriverConnect Connection String

Keyword Description

DSN

The name of the data source.

DBQ

The TNS Service Name. See Creating Oracle Database ODBC Driver TNS Service Names. For more information, see the Oracle Net Services documentation.

UID

The user login ID or user name.

PWD

The user-specified password.

5.3 Reducing Lock Timeout in a Program

The Oracle server waits indefinitely for lock conflicts between transactions, to be resolved. You can limit the amount of time that the Oracle server waits for locks to be resolved by calling the ODBC SQLSetConnectAttr function before connecting to the data source.

Specify a non-zero value for the SQL_ATTR_QUERY_TIMEOUT attribute in the ODBC SQLSetStmtAttr function. If you specify a lock timeout value using the ODBC SQLSetConnectAttr function, it overrides any value specified in the oraodbc.ini file.

See Also:

Reducing Lock Timeout for more information about specifying a value in the oraodbc.ini file

5.4 Linking with odbc32.lib (Windows) or libodbc.so (UNIX)

For Windows platforms, when you link your program, you must link it with the import library odbc32.lib.

For UNIX platforms, an ODBC application must be linked to libodbc.so.

5.5 Information about ROWID

The ODBC SQLSpecialColumns function returns information about the columns in a table. When used with the Oracle Database ODBC driver, it returns information about the Oracle ROWIDs associated with an Oracle table.

5.6 ROWID in a WHERE Clause

ROWIDs can be used in the WHERE clause of an SQL statement. However, the ROWID value must be presented in a parameter marker.

5.7 Enabling Result Sets

Oracle reference cursors (Result Sets) allow an application to retrieve data using stored procedures and stored functions. The following information identifies how to use reference cursors to enable Result Sets through ODBC.

  • The ODBC syntax for calling stored procedures must be used. Native PL/SQL is not supported through ODBC. The following identifies how to call the procedure or function without a package and within a package. The package name, in this case, is RSET.

    Procedure call: 
    {CALL Example1(?)}
    {CALL RSET.Example1(?)} 
    Function Call: 
    {? = CALL Example1(?)}
    {? = CALL RSET.Example1(?)} 
    
  • The PL/SQL reference cursor parameters are omitted when calling the procedure. For example, assume that the procedure Example2 is defined to have four parameters. Parameters 1 and 3 are reference cursor parameters and parameters 2 and 4 are character strings. The call is specified as:

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

The following example application shows how to return a Result Set using the Oracle Database ODBC driver:

/*
 * 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 Enabling EXEC Syntax

If the syntax of your SQL Server EXEC statement can be readily translated to an equivalent Oracle procedure call without change, the Oracle Database ODBC driver can translate it, if you enable this option.

The complete name of a SQL Server procedure consists of up to four identifiers:

  • server name

  • database name

  • owner name

  • procedure name

The format for the name is:

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

During the migration of the SQL Server database to Oracle, the definition of each SQL Server procedure (or function) is converted to its equivalent Oracle syntax and is defined in a schema in Oracle. Migrated procedures are often reorganized (and created in schemas) in one of these ways:

  • All procedures are migrated to one schema (the default option).

  • All procedures defined in one SQL Server database are migrated to the schema named with that database name.

  • All procedures owned by one user are migrated to the schema named with that user's name.

To support these three ways of organizing migrated procedures, you can specify one of these schema name options for translating procedure names. Object names in the translated Oracle procedure call are not case-sensitive.

5.9 Enabling Event Notification for Connection Failures in an Oracle RAC Environment

If the SQL_ORCLATTR_FAILOVER_CALLBACK and SQL_ORCLATTR_FAILOVER_HANDLE attributes of the SQLSetConnectAttr function are set when a connection failure occurs in an Oracle Real Application Clusters (Oracle RAC) Database environment, event notification is enabled. Both attributes are set using the SQLSetConnectAttr function. The symbols for the new attributes are defined in the file sqora.h.

The SQL_ORCLATTR_FAILOVER_CALLBACK attribute specifies the address of a routine to call when a failure event takes place.

The SQL_ORCLATTR_FAILOVER_HANDLE attribute specifies a context handle that is passed as a parameter in the callback routine. This attribute is necessary for the ODBC application to determine which connection the failure event is taking place on.

The function prototype for the callback routine is:

void failover_callback(void *handle, SQLINTEGER fo_code)

The 'handle' parameter is the value that was set by the SQL_ORCLATTR_FAILOVER_HANDLE attribute. Null is returned if the attribute has not been set.

The fo_code parameter identifies the failure event that is taking place. The failure events map directly to the events defined in the OCI programming interface. The list of possible events is:

  • ODBC_FO_BEGIN

  • ODBC_FO_ERROR

  • ODBC_FO_ABORT

  • ODBC_FO_REAUTH

  • ODBC_FO_END

The following is a sample program that demonstrates, using this feature:

/*
  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 Using Implicit Results Feature through ODBC

Use this option when you migrate any third party ODBC application to Oracle Database and you want to use implicit results functionality as supported by the previous vendor. The Oracle Database ODBC driver supports implicit results with stored procedures or an anonymous PL/SQL block. For the current release, implicit results are returned only for SELECT statements.

The following code example shows an example ODBC test case using an anonymous SQL script for implicit results.

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 About Supporting Oracle TIMESTAMP WITH TIME ZONE and TIMESTAMP WITH LOCAL TIME ZONE Column Type in ODBC

The time zone is dictated by the system variable ORA_SDTZ. The system variable can be set to 'OS_TZ', 'DB_TZ', or a valid time zone value. When ORA_SDTZ is set to 'OS_TZ', the operating system time zone is used. If it is set to 'DB_TZ', the default time zone set in the database is used.

By default, when ORA_SDTZ is not set, the operating system time zone is used.

Note:

When setting the ORA_SDTZ variable in a Microsoft Windows environment -- in the Registry, among system environment variables, or in a command prompt window -- do not enclose the time zone value in quotes.

See Also:

Oracle Database Globalization Support Guide for information about Datetime data types and time zone support

Fetching Data from These Time Zone Columns Using the Variable of ODBC Data Type TIMESTAMP_STRUCT

The following example demonstrates how to fetch data from TIMESTAMP WITH TIME ZONE and TIMESTAMP WITH LOCAL TIME ZONE column using the variable of ODBC datatype TIMESTAMP_STRUCT.

Example 5-1 How to Fetch Data from TIMESTAMP WITH TIME ZONE and TIMESTAMP WITH LOCAL TIME ZONE Columns Using the Variable of ODBC Data Type TIMESTAMP_STRUCT

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

Example 5-2 How to Insert Data into TIMESTAMP WITH TIME ZONE and TIMESTAMP WITH LOCAL TIME ZONE Columns

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 About the Effect of Setting ORA_SDTZ in Oracle Clients (OCI, SQL*Plus, Oracle Database ODBC Driver, and Others)

Describes the effect of setting the system variable ORA_SDTZ in Oracle Clients.

The time zone is dictated by the system variable ORA_SDTZ.

The following sections describe the effects of not setting and setting the system variable ORA_SDTZ in Oracle Clients (OCI, SQL*Plus, Oracle Database ODBC Driver, and others). The examples in these sections are run in India (GMT+5:30) time zone.

See Also:

Oracle Database Globalization Support Guide for more information about setting the session time zone

Environment Setup

To set up the environment, create the following table with TSLTZ (TIMESTAMP WITH LOCAL TIME ZONE) column and insert the value of 01/01/2016 00:00 GMT into the TSLTZ column as follows:

Example 5-3 How to Set Up the Environment

The following example sets up the environment for the example sections that follow.

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.

When ORA_SDTZ Is Not Set in the Environment

When ORA_SDTZ is not set in the environment, then the operating system (OS) time zone setting is taken as the default time zone for Oracle Clients. For example:

Example 5-4 What Happens When ORA_SDTZ Is Not Set

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

Setting ORA_SDTZ to the Operating System (OS) Timezone in the Environment

When ORA_SDTZ is set to the operating system (OS) Time zone, the Oracle Client’s user session is set to the OS time zone setting. You can either unset it in the environment or set ORA_SDTZ to OS_TZ. For example:

Example 5-5 What Happens When ORA_SDTZ Is Set to the Operating System (OS) Timezone

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

Setting ORA_SDTZ to a Specific Time Zone in the Environment

The Oracle Client can be set to retrieve the time stamp value adjusted to a specific time zone (for example, Helsinki Time Zone). To do this, you can set ORA_SDTZ to the Oracle Time Zone region name for the corresponding time zone (Oracle Time Zone Region Name for Helsinki Time Zone is Europe/Helsinki). For example:

Example 5-6 What Happens When ORA_SDTZ Is Set to a Specific Time Zone

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