// ODBC_SP.cpp : // app for testing stored procedures through ODBC // Procedures: // * set MS_SQL_DSN to your MS SQL DSN // * set MYSQL_DSN to your MySQL DSN // Target x64 platform to test 64 bit drivers // *** stored procedure // CREATE DEFINER = `root`@`localhost` PROCEDURE `TestParm`(IN InParm nvarchar (15), // OUT OutParm nvarchar (20)) // BEGIN // SELECT LName INTO OutParm FROM sys.es // WHERE FName = InParm; // END // // // *** es table // CREATE TABLE `es` ( // `FName` varchar (15) NOT NULL, // `LName` varchar (20) NOT NULL, // `EID` int (11) DEFAULT NULL, // PRIMARY KEY (`FName`) // ) ENGINE = InnoDB DEFAULT CHARSET = utf8; // // // Stored procedure // DELIMITER $$ // CREATE DEFINER = `root`@`localhost` PROCEDURE `Simple`(OUT oi int) // BEGIN // SET oi = 57; // END$$ // DELIMITER; // #include "stdafx.h" // compile with: odbc32.lib #include #include #include #include #include #include #include #define MAXBUFLEN 255 // SQLProcedureColumns constants #define COLUMN_NAME 4 #define COLUMN_TYPE 5 #define DATA_TYPE 6 #define BUFFER_LENGTH 9 #define ORDINAL_POSITION 18 // global variables SQLHENV g_henv = SQL_NULL_HENV; SQLHDBC g_hdbc = SQL_NULL_HDBC; SQLHSTMT g_hstmt = SQL_NULL_HSTMT; const SQLTCHAR* MS_SQL_DSN = L"GMB"; // MS SQL //const SQLTCHAR* MYSQL_DSN = L"MyGMB56"; // server 5.6 const SQLTCHAR* MYSQL_DSN = L"MyGMB"; // server 5.7 // forward declarations void Cleanup (unsigned int ret); bool Connect (bool bMySQL); void GetSQLDiags (SQLHSTMT h); int GetSPColumns (SQLHDBC h, SQLTCHAR* sp); void GetConnectInfo (SQLHDBC db, std::wstring& strConnectInfo); int TestParm (); int SPSelect (); int SPSelect_p (); int Simple (); // ========================================================================== int main () { int ret (0); std::cout << "*** running SPSelect ***\n"; SPSelect (); // works for GMB and MyGMB std::cout << "\n*** running SPSelect_p ***\n"; SPSelect_p (); // works for GMB and MyGMB std::cout << "\n*** running Simple ***\n"; ret = Simple (); // works for GMB not for MyGMB std::system ("pause"); return ret; } //*************************************************************************** void Cleanup (unsigned int ret) { if (g_hstmt != SQL_NULL_HSTMT) { SQLFreeHandle (SQL_HANDLE_STMT, g_hstmt); } if (g_hdbc != SQL_NULL_HDBC) { SQLDisconnect (g_hdbc); SQLFreeHandle (SQL_HANDLE_DBC, g_hdbc); } if (g_henv != SQL_NULL_HENV) { SQLFreeHandle (SQL_HANDLE_ENV, g_henv); } std::system ("pause"); exit (ret); } //*************************************************************************** bool Connect (bool bMySQL) { // Allocate the ODBC environment and save handle. RETCODE retcode = SQLAllocHandle (SQL_HANDLE_ENV, NULL, &g_henv); if ((retcode != SQL_SUCCESS_WITH_INFO) && (retcode != SQL_SUCCESS)) { printf ("SQLAllocHandle(Env) Failed\n\n"); Cleanup (1); } // Notify ODBC that this is an ODBC 3.0 app. retcode = SQLSetEnvAttr (g_henv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3, SQL_IS_INTEGER); if ((retcode != SQL_SUCCESS_WITH_INFO) && (retcode != SQL_SUCCESS)) { printf ("SQLSetEnvAttr(ODBC version) Failed\n\n"); Cleanup (2); } // Allocate ODBC connection handle and connect. retcode = SQLAllocHandle (SQL_HANDLE_DBC, g_henv, &g_hdbc); if ((retcode != SQL_SUCCESS_WITH_INFO) && (retcode != SQL_SUCCESS)) { printf ("SQLAllocHandle(hdbc1) Failed\n\n"); Cleanup (3); } SQLTCHAR* pDSN (nullptr); if (bMySQL) { pDSN = const_cast(MYSQL_DSN); } else { pDSN = const_cast(MS_SQL_DSN); } retcode = SQLConnect (g_hdbc, pDSN, SQL_NTS, (SQLTCHAR*)"", SQL_NTS, (SQLTCHAR*)"", SQL_NTS); if ((retcode != SQL_SUCCESS) && (retcode != SQL_SUCCESS_WITH_INFO)) { printf ("SQLConnect() Failed\n\n"); Cleanup (4); } return true; } //*************************************************************************** void GetSQLDiags (SQLHSTMT h) { // Get the status records. SQLWCHAR SqlState[6]; SQLWCHAR Msg[SQL_MAX_MESSAGE_LENGTH]; SQLINTEGER NativeError; SQLSMALLINT MsgLen; SQLRETURN rc; int i (1); while ((rc = SQLGetDiagRec (SQL_HANDLE_STMT, h, i, SqlState, &NativeError, Msg, sizeof (Msg), &MsgLen)) != SQL_NO_DATA) { std::wcout << L"SqlState: " << SqlState << L" NativeError: " << NativeError << L"\n" << Msg << L"\n"; ++i; } } //*************************************************************************** void GetConnectInfo (SQLHDBC db, std::wstring& strConnectInfo) { TCHAR szInfo[64]; short nResult = 0; if (SQL_SUCCEEDED (SQLGetInfo (db, SQL_DBMS_NAME, szInfo, sizeof (szInfo), &nResult))) { strConnectInfo.append (_T ("DBMS Name: ")); strConnectInfo.append (szInfo); } if (SQL_SUCCEEDED (SQLGetInfo (db, SQL_DBMS_VER, szInfo, sizeof (szInfo), &nResult))) { strConnectInfo.append (_T ("\nDBMS Version: ")); strConnectInfo.append (szInfo); } if (SQL_SUCCEEDED (SQLGetInfo (db, SQL_ODBC_VER, szInfo, sizeof (szInfo), &nResult))) { strConnectInfo.append (_T ("\nODBC Driver Manager Version: ")); strConnectInfo.append (szInfo); } if (SQL_SUCCEEDED (SQLGetInfo (db, SQL_DRIVER_VER, szInfo, sizeof (szInfo), &nResult))) { strConnectInfo.append (_T ("\nSQL driver Version: ")); strConnectInfo.append (szInfo); } if (SQL_SUCCEEDED (SQLGetInfo (db, SQL_DRIVER_ODBC_VER, szInfo, sizeof (szInfo), &nResult))) { strConnectInfo.append (_T ("\nSQL driver ODBC Version: ")); strConnectInfo.append (szInfo); } if (SQL_SUCCEEDED (SQLGetInfo (db, SQL_ODBC_INTERFACE_CONFORMANCE, szInfo, sizeof (szInfo), &nResult))) { strConnectInfo.append (_T ("\nSQL ODBC Interface Conformance: ")); // returns something like 0x3 szInfo[0] += 0x30; strConnectInfo.append (szInfo); } } //*************************************************************************** int GetSPColumns (SQLHDBC db, SQLTCHAR* sp) { unsigned int pcnt (0); SQLHSTMT hstmt (SQL_NULL_HSTMT); RETCODE retcode = SQLAllocHandle (SQL_HANDLE_STMT, db, &hstmt); if ((retcode != SQL_SUCCESS) && (retcode != SQL_SUCCESS_WITH_INFO)) { printf ("SQLAllocHandle(hstmt) Failed\n\n"); return pcnt; } //SQLRETURN SQLProcedureColumns ( // SQLHSTMT StatementHandle, // SQLCHAR * CatalogName, // SQLSMALLINT NameLength1, // SQLCHAR * SchemaName, // SQLSMALLINT NameLength2, // SQLCHAR * ProcName, // SQLSMALLINT NameLength3, // SQLCHAR * ColumnName, // SQLSMALLINT NameLength4); // Get all stored procedure columns if (SQL_SUCCEEDED (SQLProcedureColumns (hstmt, NULL, 0, NULL, 0, sp, SQL_NTS, NULL, 0))) { SQLTCHAR szParameterName[256]; SQLSMALLINT nParameterType (0); SQLSMALLINT nParameterDataType (0); SQLSMALLINT OrdinalPos (0); SQLSMALLINT BufferLen (0); SQLLEN nLen = 0; bool bFirst (true); // Bind data for fetch (parameter name, type (input, input/output, output), SQL data type) if ((SQL_SUCCEEDED (SQLBindCol (hstmt, COLUMN_NAME, SQL_C_TCHAR, szParameterName, _countof (szParameterName), &nLen))) && (SQL_SUCCEEDED (SQLBindCol (hstmt, COLUMN_TYPE, SQL_SMALLINT, &nParameterType, sizeof (nParameterType), &nLen))) && (SQL_SUCCEEDED (SQLBindCol (hstmt, DATA_TYPE, SQL_SMALLINT, &nParameterDataType, sizeof (nParameterDataType), &nLen))) && (SQL_SUCCEEDED (SQLBindCol (hstmt, BUFFER_LENGTH, SQL_SMALLINT, &BufferLen, sizeof (BufferLen), &nLen))) && (SQL_SUCCEEDED (SQLBindCol (hstmt, ORDINAL_POSITION, SQL_SMALLINT, &OrdinalPos, sizeof (OrdinalPos), &nLen))) ) { while (true) { memset (szParameterName, 0, _countof (szParameterName)); // Fetch data if (!SQL_SUCCEEDED (SQLFetch (hstmt))) { break; } ++pcnt; if (bFirst) { bFirst = false; std::wcout << "Ordinal\t Parm type Data type\tLength\tColumn Name\n"; } std::wcout << OrdinalPos << "\t\t" << nParameterType << "\t\t" << nParameterDataType << "\t" << BufferLen << "\t" << szParameterName << "\n"; } } } return pcnt; } // ************************************************************************** // Simple SP with 1 integer out parm // int Simple () { bool mysql (true); if (!Connect (mysql)) { return 1; } RETCODE retcode (SQL_ERROR); std::wstring sinfo; GetConnectInfo (g_hdbc, sinfo); std::wcout << sinfo.c_str () << L"\n\n"; // *** list out the SP parameters *** int pcnt = GetSPColumns (g_hdbc, L"Simple"); std::wcout << L"\nSP column count: " << pcnt << L"\n\n"; // Allocate statement handle. retcode = SQLAllocHandle (SQL_HANDLE_STMT, g_hdbc, &g_hstmt); if ((retcode != SQL_SUCCESS) && (retcode != SQL_SUCCESS_WITH_INFO)) { printf ("SQLAllocHandle(hstmt1) Failed\n\n"); Cleanup (5); } SDWORD OutParm (0); SQLLEN cbParm1 = SQL_NTS; // SQLBindParameter variables. if (mysql) { // Bind the output parameter to variable OutParm. retcode = SQLBindParameter (g_hstmt, 1, SQL_PARAM_OUTPUT, SQL_C_SLONG, SQL_INTEGER, 0, 0, &OutParm, 0, &cbParm1); if ((retcode != SQL_SUCCESS) && (retcode != SQL_SUCCESS_WITH_INFO)) { GetSQLDiags (g_hstmt); printf ("SQLBindParameter(OutParm) Failed\n\n"); Cleanup (7); } } else { SWORD RetParm (0); SQLLEN cbRetParm (SQL_DATA_AT_EXEC); // Bind the return code to variable RetParm. retcode = SQLBindParameter (g_hstmt, 1, SQL_PARAM_OUTPUT, SQL_C_SSHORT, SQL_INTEGER, 0, 0, &RetParm, 0, nullptr); if ((retcode != SQL_SUCCESS) && (retcode != SQL_SUCCESS_WITH_INFO)) { GetSQLDiags (g_hstmt); printf ("SQLBindParameter(RetParm) Failed\n\n"); Cleanup (6); } SQLLEN cbOutParm (SQL_DATA_AT_EXEC); // Bind the output parameter to variable OutParm. retcode = SQLBindParameter (g_hstmt, 2, SQL_PARAM_OUTPUT, SQL_C_SLONG, SQL_INTEGER, 0, 0, &OutParm, 0, nullptr); if ((retcode != SQL_SUCCESS) && (retcode != SQL_SUCCESS_WITH_INFO)) { GetSQLDiags (g_hstmt); printf ("SQLBindParameter(OutParm) Failed\n\n"); Cleanup (7); } } // Execute the command. std::wstring sql (L"{? = call \"Simple\" (?)}"); if (mysql) { sql = L"{call Simple (?)}"; } retcode = SQLExecDirect (g_hstmt, (SQLTCHAR*)sql.c_str (), SQL_NTS); if ((retcode != SQL_SUCCESS) && (retcode != SQL_SUCCESS_WITH_INFO)) { GetSQLDiags (g_hstmt); printf ("SQLExecDirect Failed\n\n"); Cleanup (8); } SQLSMALLINT nCols = 0; SQLNumResultCols (g_hstmt, &nCols); // Show parameters are not filled. //printf ("Before result sets cleared: RetCode = %d, OutParm = %d.\n", sParm1, sParm2); //printf ("Before result sets cleared: %d\n", OutParm); std::cout << "Before result sets cleared: " << OutParm << "\n"; // Clear any result sets generated. while ((retcode = SQLMoreResults (g_hstmt)) != SQL_NO_DATA) ; // Show parameters are now filled. // printf ("After result sets drained: RetCode = %d, OutParm = %d.\n", sParm1, sParm2); // printf ("After result sets drained: %d\n", OutParm); std::cout << "After result sets drained: " << OutParm << "\n"; // Clean up. SQLFreeHandle (SQL_HANDLE_STMT, g_hstmt); SQLDisconnect (g_hdbc); SQLFreeHandle (SQL_HANDLE_DBC, g_hdbc); SQLFreeHandle (SQL_HANDLE_ENV, g_henv); return 0; } // ************************************************************************** // SP with no parms, simple select // int SPSelect () { bool mysql (true); if (!Connect (mysql)) { return 1; } RETCODE retcode (SQL_ERROR); std::wstring sinfo; GetConnectInfo (g_hdbc, sinfo); std::wcout << sinfo.c_str () << L"\n\n"; // *** list out the SP parameters *** int pcnt = GetSPColumns (g_hdbc, L"SPSelect"); std::wcout << L"\nSP column count: " << pcnt << L"\n\n"; // Allocate statement handle. retcode = SQLAllocHandle (SQL_HANDLE_STMT, g_hdbc, &g_hstmt); if ((retcode != SQL_SUCCESS) && (retcode != SQL_SUCCESS_WITH_INFO)) { printf ("SQLAllocHandle(hstmt1) Failed\n\n"); Cleanup (5); } if (!mysql) { SWORD RetParm (0); SQLLEN cbRetParm (SQL_DATA_AT_EXEC); // Bind the return code to variable RetParm. retcode = SQLBindParameter (g_hstmt, 1, SQL_PARAM_OUTPUT, SQL_C_SSHORT, SQL_INTEGER, 0, 0, &RetParm, 0, nullptr); if ((retcode != SQL_SUCCESS) && (retcode != SQL_SUCCESS_WITH_INFO)) { GetSQLDiags (g_hstmt); printf ("SQLBindParameter(RetParm) Failed\n\n"); Cleanup (6); } } // Execute the command. std::wstring sql (L"{? = call \"SPSelect\"}"); if (mysql) { sql = L"{call SPSelect}"; } retcode = SQLExecDirect (g_hstmt, (SQLTCHAR*)sql.c_str (), SQL_NTS); if ((retcode != SQL_SUCCESS) && (retcode != SQL_SUCCESS_WITH_INFO)) { GetSQLDiags (g_hstmt); printf ("SQLExecDirect Failed\n\n"); Cleanup (8); } SQLSMALLINT nCols = 0; SQLNumResultCols (g_hstmt, &nCols); if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) { #define FNAME_LEN 15 // the bind length must include the terminating null #define LNAME_LEN 20 SQLCHAR szFName[FNAME_LEN + 1]; SQLCHAR szLName[LNAME_LEN + 1]; SQLINTEGER EID; // Bind columns 1, 2, and 3 retcode = SQLBindCol (g_hstmt, 1, SQL_C_CHAR, &szFName, FNAME_LEN + 1, nullptr); retcode = SQLBindCol (g_hstmt, 2, SQL_C_CHAR, szLName, LNAME_LEN + 1, nullptr); retcode = SQLBindCol (g_hstmt, 3, SQL_C_LONG, &EID, 0, nullptr); // Fetch and print each row of data. On an error, display a message and exit. while (retcode == SQL_SUCCESS) { retcode = SQLFetch (g_hstmt); if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO) { GetSQLDiags (g_hstmt); } if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) { wprintf (L"%S %S %d\n", szFName, szLName, EID); } else { break; } } } // Clean up. SQLFreeHandle (SQL_HANDLE_STMT, g_hstmt); SQLDisconnect (g_hdbc); SQLFreeHandle (SQL_HANDLE_DBC, g_hdbc); SQLFreeHandle (SQL_HANDLE_ENV, g_henv); return 0; } // ************************************************************************** // SP with input parms, simple select // int SPSelect_p () { bool mysql (true); if (!Connect (mysql)) { return 1; } RETCODE retcode (SQL_ERROR); std::wstring sinfo; GetConnectInfo (g_hdbc, sinfo); std::wcout << sinfo.c_str () << L"\n\n"; // *** list out the SP parameters *** int pcnt = GetSPColumns (g_hdbc, L"SPSelect_p"); std::wcout << L"\nSP column count: " << pcnt << L"\n\n"; // Allocate statement handle. retcode = SQLAllocHandle (SQL_HANDLE_STMT, g_hdbc, &g_hstmt); if ((retcode != SQL_SUCCESS) && (retcode != SQL_SUCCESS_WITH_INFO)) { printf ("SQLAllocHandle(hstmt1) Failed\n\n"); Cleanup (5); } std::wstring InParm (L"Gary"); // didn't work for GMB std::string InParm_a ("Gary"); if (mysql) { // Bind the input parameter to variable InParm. retcode = SQLBindParameter (g_hstmt, 1, SQL_PARAM_INPUT, SQL_CHAR, SQL_VARCHAR, 15, 0, (SQLPOINTER)InParm_a.c_str (), InParm_a.length (), NULL); if ((retcode != SQL_SUCCESS) && (retcode != SQL_SUCCESS_WITH_INFO)) { printf ("SQLBindParameter(InParm) Failed\n\n"); GetSQLDiags (g_hstmt); Cleanup (7); } } else { SWORD RetParm (0); SQLLEN cbRetParm (SQL_DATA_AT_EXEC); // Bind the return code to variable RetParm. retcode = SQLBindParameter (g_hstmt, 1, SQL_PARAM_OUTPUT, SQL_C_SSHORT, SQL_INTEGER, 0, 0, &RetParm, 0, nullptr); if ((retcode != SQL_SUCCESS) && (retcode != SQL_SUCCESS_WITH_INFO)) { GetSQLDiags (g_hstmt); printf ("SQLBindParameter(RetParm) Failed\n\n"); Cleanup (6); } // Bind the input parameter to variable InParm. retcode = SQLBindParameter (g_hstmt, 2, SQL_PARAM_INPUT, SQL_CHAR, SQL_VARCHAR, 15, 0, (SQLPOINTER)InParm_a.c_str (), InParm_a.length (), nullptr); if ((retcode != SQL_SUCCESS) && (retcode != SQL_SUCCESS_WITH_INFO)) { printf ("SQLBindParameter(InParm) Failed\n\n"); GetSQLDiags (g_hstmt); Cleanup (7); } } // Execute the command. std::wstring sql (L"{? = call \"SPSelect_p\" (?)}"); if (mysql) { sql = L"{call SPSelect_p (?)}"; } retcode = SQLExecDirect (g_hstmt, (SQLTCHAR*)sql.c_str (), SQL_NTS); if ((retcode != SQL_SUCCESS) && (retcode != SQL_SUCCESS_WITH_INFO)) { GetSQLDiags (g_hstmt); printf ("SQLExecDirect Failed\n\n"); Cleanup (8); } SQLSMALLINT nCols = 0; SQLNumResultCols (g_hstmt, &nCols); if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) { #define FNAME_LEN 15 // the bind length must include the terminating null #define LNAME_LEN 20 SQLCHAR szFName[FNAME_LEN + 1]; SQLCHAR szLName[LNAME_LEN + 1]; SQLINTEGER EID; // Bind columns 1, 2, and 3 retcode = SQLBindCol (g_hstmt, 1, SQL_C_CHAR, &szFName, FNAME_LEN + 1, nullptr); retcode = SQLBindCol (g_hstmt, 2, SQL_C_CHAR, szLName, LNAME_LEN + 1, nullptr); retcode = SQLBindCol (g_hstmt, 3, SQL_C_LONG, &EID, 0, nullptr); // Fetch and print each row of data. On an error, display a message and exit. while (retcode == SQL_SUCCESS) { retcode = SQLFetch (g_hstmt); if (retcode == SQL_ERROR) { GetSQLDiags (g_hstmt); } if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) { wprintf (L"%S %S %d\n", szFName, szLName, EID); } else { std::cout << "\n"; break; } } } // Clean up. SQLFreeHandle (SQL_HANDLE_STMT, g_hstmt); SQLDisconnect (g_hdbc); SQLFreeHandle (SQL_HANDLE_DBC, g_hdbc); SQLFreeHandle (SQL_HANDLE_ENV, g_henv); return 0; } // ************************************************************************** // SP TestParm, takes first name as input, output last name, for MS SQL // returns and int // int TestParm () { bool mysql (true); if (!Connect (mysql)) { return 1; } RETCODE retcode (SQL_ERROR); // Allocate statement handle. retcode = SQLAllocHandle (SQL_HANDLE_STMT, g_hdbc, &g_hstmt); if ((retcode != SQL_SUCCESS) && (retcode != SQL_SUCCESS_WITH_INFO)) { printf ("SQLAllocHandle(hstmt1) Failed\n\n"); Cleanup (5); } int cnt = GetSPColumns (g_hdbc, L"TestParm"); std::cout << "\nSP column count: " << cnt << "\n\n"; // SQLBindParameter variables. SWORD sParm1 = 0, sParm2 (1); // Bind the return code to variable sParm1. /* SQLRETURN SQLBindParameter ( SQLHSTMT StatementHandle, SQLUSMALLINT ParameterNumber, SQLSMALLINT InputOutputType, SQLSMALLINT ValueType, SQLSMALLINT ParameterType, SQLULEN ColumnSize, SQLSMALLINT DecimalDigits, SQLPOINTER ParameterValuePtr, SQLLEN BufferLength, SQLLEN * StrLen_or_IndPtr); */ std::wstring OutParm; std::wstring InParm (L"Gary"); std::string OutParm_a; std::string InParm_a ("Gary"); if (mysql) { // Bind the input parameter to variable InParm. retcode = SQLBindParameter (g_hstmt, 1, SQL_PARAM_INPUT, SQL_CHAR, SQL_VARCHAR, 15, 0, (SQLPOINTER)InParm_a.c_str (), InParm_a.length (), NULL); if ((retcode != SQL_SUCCESS) && (retcode != SQL_SUCCESS_WITH_INFO)) { printf ("SQLBindParameter(InParm) Failed\n\n"); GetSQLDiags (g_hstmt); Cleanup (7); } // Bind the output parameter to variable OutParm. retcode = SQLBindParameter (g_hstmt, 2, SQL_PARAM_OUTPUT, SQL_CHAR, SQL_VARCHAR, 20, 0, (SQLPOINTER)OutParm_a.c_str (), 20, NULL); if ((retcode != SQL_SUCCESS) && (retcode != SQL_SUCCESS_WITH_INFO)) { printf ("SQLBindParameter(OutParm) Failed\n\n"); Cleanup (7); } } else { retcode = SQLBindParameter (g_hstmt, 1, SQL_PARAM_OUTPUT, SQL_C_SSHORT, SQL_INTEGER, 0, 0, &sParm1, 0, NULL); if ((retcode != SQL_SUCCESS) && (retcode != SQL_SUCCESS_WITH_INFO)) { printf ("SQLBindParameter(sParm1) Failed\n\n"); GetSQLDiags (g_hstmt); Cleanup (6); } // Bind the output parameter to variable InParm. retcode = SQLBindParameter (g_hstmt, 2, SQL_PARAM_INPUT, SQL_C_WCHAR, SQL_WVARCHAR, 15, 0, (SQLPOINTER)InParm.c_str (), InParm.length (), NULL); if ((retcode != SQL_SUCCESS) && (retcode != SQL_SUCCESS_WITH_INFO)) { printf ("SQLBindParameter(InParm) Failed\n\n"); GetSQLDiags (g_hstmt); Cleanup (7); } // Bind the output parameter to variable OutParm. retcode = SQLBindParameter (g_hstmt, 3, SQL_PARAM_OUTPUT, SQL_C_WCHAR, SQL_WVARCHAR, 20, 0, (SQLPOINTER)OutParm.c_str (), 20, NULL); if ((retcode != SQL_SUCCESS) && (retcode != SQL_SUCCESS_WITH_INFO)) { printf ("SQLBindParameter(OutParm) Failed\n\n"); Cleanup (7); } } // Execute the command. std::wstring sql (L"{? = call \"TestParm\" (?,?)}"); if (mysql) { sql = L"{call TestParm (?,?)}"; } retcode = SQLExecDirect (g_hstmt, (SQLTCHAR*)sql.c_str (), SQL_NTS); if ((retcode != SQL_SUCCESS) && (retcode != SQL_SUCCESS_WITH_INFO)) { printf ("SQLExecDirect Failed\n\n"); // Get the status records. GetSQLDiags (g_hstmt); Cleanup (8); } // Show parameters are not filled. //printf ("Before result sets cleared: RetCode = %d, OutParm = %d.\n", sParm1, sParm2); printf ("Before result sets cleared: RetCode = %d\n", sParm1); std::wcout << L"OutParm: " << OutParm.c_str () << L"\n"; // Clear any result sets generated. while ((retcode = SQLMoreResults (g_hstmt)) != SQL_NO_DATA) ; // Show parameters are now filled. // printf ("After result sets drained: RetCode = %d, OutParm = %d.\n", sParm1, sParm2); printf ("After result sets drained: RetCode = %d\n", sParm1); std::wcout << L"OutParm: " << OutParm.c_str () << L"\n"; // Clean up. SQLFreeHandle (SQL_HANDLE_STMT, g_hstmt); SQLDisconnect (g_hdbc); SQLFreeHandle (SQL_HANDLE_DBC, g_hdbc); SQLFreeHandle (SQL_HANDLE_ENV, g_henv); return 0; }