// To run the test, the first parameter must be an ODBC connection string // in quotes: // // odbcbug "dsn=mydsn;uid=myuid;pwd=mypwd" // #include #include #include #include #include "odbc.h" #if ODBCVER < 0x0350 #error "***** This module requires ODBC 3.5 or higher *****" #endif typedef unsigned int uint32; typedef uint32 _W64 a_ptrint; typedef struct an_odbc_info { SQLHENV env; } an_odbc_info, *p_odbc_info; typedef struct an_odbc_conn { p_odbc_info info; SQLHDBC dbc; int kill; int done; } an_odbc_conn, *p_odbc_conn; #define _c( e, d, s ) \ if( ret != 0 && ret != SQL_SUCCESS_WITH_INFO ) { \ printf( "error at line: %d\n", __LINE__ ); \ ODBC_error( e, d, s ); \ goto done; \ } void ODBC_error ( /* Obtain ODBC Error */ /***************/ HENV henv, /* ODBC Environment */ HDBC hdbc, /* ODBC Connection Handle */ HSTMT hstmt ) /* ODBC SQL Handle */ { UCHAR sqlstate[10]; UCHAR errmsg[SQL_MAX_MESSAGE_LENGTH]; SDWORD nativeerr; SWORD actualmsglen; RETCODE rc; loop: rc = SQLError((SQLHENV)henv, (SQLHDBC)hdbc, (SQLHSTMT)hstmt, sqlstate, &nativeerr, errmsg, SQL_MAX_MESSAGE_LENGTH - 1, &actualmsglen); if( rc == SQL_ERROR ) { printf ("SQLError failed!\n"); return; } if( rc != SQL_NO_DATA_FOUND ) { printf ("SQLSTATE = %s\n",sqlstate); printf ("NATIVE ERROR = %d\n",nativeerr); errmsg[actualmsglen] = '\0'; printf ("MSG = %s\n\n",errmsg); goto loop; } } p_odbc_info Init( void ) /**********************/ { p_odbc_info info; RETCODE ret; info = (p_odbc_info) malloc( sizeof( *info ) ); if( info != NULL ) { ret = SQLAllocHandle( SQL_HANDLE_ENV, SQL_NULL_HANDLE, &info->env ); _c( info->env, NULL, NULL ); ret = SQLSetEnvAttr( info->env, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3, 0 ); _c( info->env, NULL, NULL ); } done: return( info ); } void Fini( p_odbc_info info ) /***************************/ { RETCODE ret; // Clean up any resources and shut the module down. if( info->env != SQL_NULL_HENV ) { ret = SQLFreeHandle( SQL_HANDLE_ENV, info->env ); } } p_odbc_conn Connect( /******************/ p_odbc_info info, char * conn_str ) { p_odbc_conn conn; SQLSMALLINT conn_str_len; char out_str[ 2048 ]; RETCODE ret = SQL_ERROR; conn = (p_odbc_conn) malloc( sizeof( an_odbc_conn ) ); if( conn == NULL ) { goto done; } conn->info = info; conn->dbc = SQL_NULL_HDBC; conn->done = FALSE; ret = SQLAllocHandle( SQL_HANDLE_DBC, info->env, &conn->dbc ); _c( info->env, conn->dbc, NULL ); ret = SQLDriverConnect( conn->dbc, 0, (SQLCHAR *)conn_str, SQL_NTS, (SQLCHAR *)out_str, sizeof( out_str ), &conn_str_len, SQL_DRIVER_NOPROMPT ); _c( info->env, conn->dbc, NULL ); if( ( ret != SQL_SUCCESS ) && ( ret != SQL_SUCCESS_WITH_INFO ) ) { conn = NULL; goto done; } // Use transactions instead of auto-commit ret = SQLSetConnectAttr( conn->dbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)SQL_AUTOCOMMIT_OFF, SQL_IS_UINTEGER ); _c( info->env, conn->dbc, NULL ); done: return( conn ); } void Disconnect( p_odbc_conn conn ) /*********************************/ { if( conn != NULL ) { if( conn->dbc != SQL_NULL_HDBC ) { SQLDisconnect( conn->dbc ); SQLFreeHandle( SQL_HANDLE_DBC, conn->dbc ); } free( conn ); } } SQLINTEGER ShrinkStr( const wchar_t * src, char * dest ) /******************************************************/ { SQLINTEGER len = 0; while( TRUE ) { *dest = *(char *)src; if( *src == L'\0' ) { break; } dest++; src++; len++; } dest[len] = '\0'; return( len ); } void dumpHex( wchar_t * c1, SQLINTEGER len ) /******************************************/ { unsigned char str[ 2000 ]; char buf[ 5200 ]; char * p = buf; int i = 0; strncpy( (char *)str, (char *)c1, len ); p += sprintf( p, "Fetched value in hex:\n " ); for( i = 0; i < len; i++ ) { p += sprintf( p, "%02X ", str[ i ] ); } printf( buf ); } void cpfromansi(wchar_t *des, char *src) { int len = strlen(src); int i; for(i=0; i < len; i++) { des[i] = src[i]; } des[i] = 0; } void ShowBug( p_odbc_conn conn, char * catalog, char * schema, char * procedure ) /*****************************************/ { SQLHSTMT stmt; RETCODE ret; SQLSMALLINT size = 0; ret = SQLAllocHandle( SQL_HANDLE_STMT, conn->dbc, &stmt ); ret = SQLProcedures( stmt, catalog, sizeof(char)*strlen(catalog), schema, sizeof(char)*strlen(schema), procedure, sizeof(char)*strlen(procedure) ); ret = SQLFreeStmt( stmt, SQL_RESET_PARAMS ); ret = SQLNumResultCols( stmt, &size ); done: return; } int main( int argc, char *argv[] ) /********************************/ { p_odbc_info info = NULL; p_odbc_conn conn = NULL; if( argc < 3 ){ printf( "Usage:\n" ); printf( "\todbcbug \n" ); return -1; } info = Init(); if( info != NULL ) { argc = argc; // Unused conn = Connect( info, argv[ 1 ] ); if( conn != NULL ) { // ============= ShowBug( conn, "", argv[2], argv[3] ); // ============= Disconnect( conn ); } Fini( info ); } return( 0 ); }