How do I get a connection? - c++

ALL,
I am trying to get a connection thru the ODBC call to the database. The DSN is configured and I can run the DB engine without any issues. But trying to connect from my program thru the SQLConnect() call fails.
I strongly suspect that this is because I didn't convert the dsn properly from std::string to the SQLCHAR *. Here is the code I'm using:
std::string selectedDSN;
SQLCHAR *dsn;
dsn = new unsigned char[selectedDSN.length() + 1];
strcpy( (char *) dsn, selectedDSN );
ret = SQLConnect( hdbc, (SQLCHAR *) dsn, SQL_NTS, (SQLCHAR*) NULL, 0, NULL, 0 );
if( ret != SQL_SUCCESS || ret != SQL_SUCCESS_WITH_INFO )
{
ret = SQLGetDiagRec( SQL_HANDLE_ENV, env, 1, sqlstate, &native_error, msg, sizeof( msg ), &msglen );
std::wstring temp( msg );
std::string t( temp.begin(), temp.end() );
errorMsg = t;
result = 1;
}
The conversion I have here is from this link. And the following call to
However the "ret" variable is set to -1 and the following call to SQLGetDiagRec() return 100 (SQL_SUCCESS_WITH_INFO.

The problem is here:
if( ret != SQL_SUCCESS || ret != SQL_SUCCESS_WITH_INFO )
This expression is always true. Change to:
if( ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO )
^^

Related

EAGAIN in ZMQ extended request reply

I'm trying to create a REQ <--> Router <--> Dealer <--> REP communication in C++. The child process binds the router and dealer, proxies between router and dealer, connects the REP to the dealer and waits for a message with zmq_recv.
The parent process connects a REQ to the router and tries to send a message, however I'm getting a zmq_send error in parent: Resource temporarily unavailable (which is EAGAIN). According to zmq_send docs, EAGAIN means:
Non-blocking mode was requested and the message cannot be sent at the moment.
However the message does get sent since it is received in the child process. Why does it return that errno?
Here is the MCVE:
#include <zmq.h>
#include <iostream>
#include <sys/types.h>
#include <unistd.h>
#include <assert.h>
#include <thread>
#include <stdio.h>
int main() {
char connect_path[35];
int rc;
int msg;
pid_t child_pid = fork();
if (child_pid == 0) {
// Child
void* child_context = zmq_ctx_new ();
if (child_context == NULL) {
std::cerr << "\nChild context error\n";
}
void* router = zmq_socket(child_context, ZMQ_ROUTER);
if (router == NULL) {
perror("zmq_socket of type router error");
}
char bind_path[35];
snprintf(bind_path, sizeof(bind_path), "ipc:///tmp/zmqtest%d-router", getpid());
rc = zmq_bind(router, bind_path);
assert (rc == 0);
void* dealer = zmq_socket(child_context, ZMQ_DEALER);
if (dealer == NULL) {
perror("zmq_socket of type dealer error");
}
snprintf(bind_path, sizeof(bind_path), "ipc:///tmp/zmqtest%d-dealer", getpid());
rc = zmq_bind(dealer, bind_path);
assert (rc == 0);
std::thread z_proxy (zmq_proxy, router, dealer, nullptr);
z_proxy.detach();
void* rep_socket = zmq_socket (child_context, ZMQ_REP);
if (rep_socket == NULL) {
perror("zmq_socket of type rep error");
}
snprintf(connect_path, sizeof(connect_path), "ipc:///tmp/zmqtest%d-dealer", getpid());
rc = zmq_connect(rep_socket, connect_path);
assert (rc == 0);
while(1) {
if (zmq_recv (rep_socket, &msg, sizeof(msg), 0) != 0) {
perror("zmq_recv error");
}
printf("\nReceived msg %d in process %d\n", msg, getpid());
break;
}
if (zmq_close(rep_socket) != 0) {
perror("zmq_close of rep_socket in child error");
}
if (zmq_ctx_term(child_context) != 0) {
perror("zmq_ctx_term of child_context error");
}
} else {
// Parent
sleep(1);
void* parent_context = zmq_ctx_new ();
if (parent_context == NULL) {
std::cerr << "\nParent ctx error\n";
}
void* req_socket = zmq_socket (parent_context, ZMQ_REQ);
if (req_socket == NULL) {
perror("zmq_socket of type req error in parent");
}
snprintf(connect_path, sizeof(connect_path), "ipc:///tmp/zmqtest%d-router", child_pid);
rc = zmq_connect(req_socket, connect_path);
assert (rc == 0);
msg = 30;
if (zmq_send (req_socket, &msg, sizeof(msg), 0) != 0) {
perror("zmq_send error in parent");
}
if (zmq_close(req_socket) != 0) {
perror("zmq_close of req_socket in parent error");
}
if (zmq_ctx_term(parent_context) != 0) {
perror("zmq_ctx_term of parent_context error");
}
}
}
Step 1: Make a trivial test:
Well, as a minimum point, there ought be this sort of test-en-Queueing first:
rc = zmq_send ( req_socket, "A_TEST_BLOCK", 12, ZMQ_DONTWAIT );
printf ( "INF: zmq_send ( req_socket, "A_TEST_BLOCK", 12, ZMQ_DONTWAIT )\nZMQ: returned rc == %d\nZMQ: zmq_errno ~ %s\n",
rc,
zmq_strerror ( zmq_errno() )
);
.
Step 2: post the printed outputs
Next, if there are any "missed" shots, the error-analysis may advise on potential reason(s)
( if and only if the parent_ctx indeed rejected to even accept the data from a simplest ever zmq_send() call into it's internal queueing facility with an explicit reason for having done so ).
Otherwise we know nothing ( and the ZMQ_DONTWAIT flag is not the reason here ).
As the test was run, it yielded:
INF: zmq_send ( req_socket, 'A_TEST_BLOCK', 12, ZMQ_DONTWAIT )
ZMQ: returned rc == 12
ZMQ: zmq_errno ~ Resource temporarily unavailable
Step 3:
The test has confirmed, as per documentation:
The zmq_send() function shall return number of bytes in the message if successful.
So, let's dig a step deeper:
int major, minor, patch;
zmq_version ( &major, &minor, &patch );
printf ( "INF: current ØMQ version is %d.%d.%d\nZMQ: zmq_errno ~ %s\n",
major, minor, patch,
zmq_strerror ( zmq_errno() )
);
Step 4:
In case the bleeding-edge API-updates do not conform to the published API-specification, document the incident:
printf ( "EXPECT( NO ERROR, ON START ): zmq_errno ~ %s\n",
zmq_strerror ( zmq_errno() )
);
printf ( "EXPECT( <major>.<minor>.<patch> ): zmq_version ~\n" );
int major, minor, patch
zmq_version ( &major, &minor, &patch );
printf ( "INF: current ØMQ version is %d.%d.%d\nZMQ: zmq_errno ~ %s\n",
major, minor, patch
)
printf ( "EXPECT( NO ERROR ): zmq_errno ~ %s\n",
zmq_strerror ( zmq_errno() )
);
printf ( "EXPECT( NO ERROR ): zmq_send() ~ %s\n" );
rc = zmq_send ( req_socket, "A_TEST_BLOCK", 12, ZMQ_DONTWAIT );
printf ( "INF: zmq_send ( req_socket, "A_TEST_BLOCK", 12, ZMQ_DONTWAIT )\nZMQ: returned rc == %d which ouhgt be == 12, is it?\n",
rc
);
printf ( "EXPECT( NO ERROR ): zmq_errno ~ %s\n",
zmq_strerror ( zmq_errno() )
);
and feel free to file an issue, if unexpected results appear.

SQLExecute always returning "[Microsoft][SQL Server Native Client 10.0]String data, right truncation" in parameters more than 8k sized

When I execute the SQLExecute function it always returns me "[Microsoft][SQL Server Native Client 10.0]String data, right truncation" when my parameter has more than 8k bytes. I will paste the code below.
What I'm trying to do: to store a XML file in a column declared as varbinary(max) through a Stored Procedure via ODBC drivers (Visual C++ 2008) in a SQL Server 2008 R2.
The SP converts from varchar to varbinary calling SET #XML_FILE_BIN = CONVERT(VARBINARY(MAX), #XML_FILE)
It works fine if I try it pasting the whole XML int the SQL Server Management Studio.
I think something is wrong the binding in SQLBindParameter.
The code:
char* cXmlBuf; it contains my buffer
retcode = SQLBindParameter(
hstmt, //StatementHandle
1, //ParameterNumber
SQL_PARAM_INPUT, //InputOutputType
SQL_C_CHAR, //ValueType
SQL_CHAR, //ParameterType
SQL_DESC_LENGTH, //ColumnSize
0, //DecimalDigits
cXmlBuf, //ParameterValuePtr
bufLenght, //BufferLength
&cbXml //StrLen_or_IndPtr
);
if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO)
return;
SWORD id = 0;
SQLINTEGER cbId = 0;
retcode = SQLBindParameter(hstmt, 2, SQL_PARAM_OUTPUT, SQL_C_SSHORT, SQL_INTEGER, 0, 0, &id, 0, &cbId);
if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO)
return;
retcode = SQLPrepare(hstmt, (SQLCHAR*)"{CALL MY_STORE_PROC(?, ?)}", SQL_NTS);
if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO)
return;
retcode = SQLFreeStmt(hstmt, SQL_CLOSE); // Clear any cursor state
retcode = SQLExecute(hstmt);
//in this part retcode is -1 and "[Microsoft][SQL Server Native Client
//10.0]String data, right truncation" is returned if my XML buffer
//has more than 8k
Found it!
I've declared SQLINTEGER cbXml = SQL_NTS; instead SQLLEN cbXml = 0;
Thanks.

C++ copy sqlite blob from one database to another

I am trying to copy some blob data from one sqlite table to another in C++. However, once the data has been copied over to the new table, it seems to be getting corrupted. The said data contains some jpeg images. The code I am using to copy from TABLE1 to TABLE2 is shown below:
// Read the blob from the database
int64_t rowID = 0;
sscanf( id.c_str(), "%llu", &rowID );
sqlite3_blob* blobHandle = NULL;
if( sqlite3_blob_open( m_dbHandle_temp, "main", m_currentTileTable.c_str(), "tile_data", rowID, 0, &blobHandle ) != SQLITE_OK )
{
sqlite3_blob_close( blobHandle ); // An SQLite blob will be initialised regardless of the success of 'sqlite3_blob_open'
return false;
}
tiles_insert_statement.append( ")" );
// Copy blob to database
sqlite3_stmt *stmt = 0;
const char* tail;
sqlite3_prepare_v2( m_dbHandle, tiles_insert_statement.c_str(), strlen( tiles_insert_statement.c_str() )+1, &stmt, &tail );
int bindSuccess = sqlite3_bind_blob( stmt, 1, blobHandle, sqlite3_blob_bytes( blobHandle ), SQLITE_TRANSIENT );
if( sqlite3_step( stmt ) != SQLITE_DONE )
printf( "Error message: %s\n", sqlite3_errmsg( m_dbHandle ) );
sqlite3_finalize( stmt );
// close handles
sqlite3_blob_close( blobHandle );
Is there anything I am doing wrong in the above code, the reason I say that it is getting corrupted is because, I am reading the blobs on an android device to be displayed in an image viewer. The blobs in the TABLE 1 can be read and displayed fine, however the ones in TABLE 2 do not display anything. Any help is greatly appreaciated.
SOLUTION:
// Read the blob from the database
int64_t rowID = 0;
sscanf( id.c_str(), "%llu", &rowID );
sqlite3_blob* blobHandle = NULL;
if( sqlite3_blob_open( m_dbHandle_temp, "main", m_currentTileTable.c_str(), "tile_data", rowID, 0, &blobHandle ) != SQLITE_OK )
{
sqlite3_blob_close( blobHandle ); // An SQLite blob will be initialised regardless of the success of 'sqlite3_blob_open'
return false;
}
unsigned int length = sqlite3_blob_bytes( blobHandle );
// TODO - instances of this class OWN the buffer.
// Delete the buffer in the destructor ;)
unsigned char* buffer = new unsigned char[ length ];
if( sqlite3_blob_read( blobHandle, buffer, length, 0 ) != SQLITE_OK )
{
return false;
}
tiles_insert_statement.append( ")" );
sqlite3_stmt *stmt = 0;
const char* tail;
sqlite3_prepare_v2( m_dbHandle, tiles_insert_statement.c_str(), strlen( tiles_insert_statement.c_str() )+1, &stmt, &tail );
int bindSuccess = sqlite3_bind_blob( stmt, 1, buffer, length, SQLITE_TRANSIENT );
if( sqlite3_step( stmt ) != SQLITE_DONE )
printf( "Error message: %s\n", sqlite3_errmsg( m_dbHandle ) );
sqlite3_finalize( stmt );
// close handles
sqlite3_blob_close( blobHandle );
sqlite3_bind_blob expects a pointer to the actual blob data; it is not possible to use a blob handle for that.
To get the blob data as a memory chunk, execute a query like SELECT tile_data FROM MyTable WHERE ... and read the value with sqlite3_column_blob.

How do I find the port name for a bluetooth device with a specific device name?

How do I find the port name for a bluetooth device with a specific device name?
I have this code, which enumerates all bluetooth devices, but doesn't give me their port name:
HBLUETOOTH_DEVICE_FIND founded_device;
BLUETOOTH_DEVICE_INFO device_info;
device_info.dwSize = sizeof(device_info);
BLUETOOTH_DEVICE_SEARCH_PARAMS search_criteria;
search_criteria.dwSize = sizeof(BLUETOOTH_DEVICE_SEARCH_PARAMS);
search_criteria.fReturnAuthenticated = TRUE;
search_criteria.fReturnRemembered = FALSE;
search_criteria.fReturnConnected = FALSE;
search_criteria.fReturnUnknown = FALSE;
search_criteria.fIssueInquiry = FALSE;
search_criteria.cTimeoutMultiplier = 0;
founded_device = BluetoothFindFirstDevice(&search_criteria, &device_info);
if(founded_device == NULL)
return -1;
do {
wstring ws = device_info.szName;
cout << string(ws.begin(), ws.end()) << endl;
} while (BluetoothFindNextDevice(founded_device, &device_info));
And then I have this code, which enumerates all port names but doesn't give me the device name:
DWORD bytesNeeded = 0;
DWORD portCount = 0;
BOOL ret = EnumPorts(nullptr, 2, nullptr, 0, &bytesNeeded, &portCount);
BYTE *ports = new BYTE[bytesNeeded];
if(EnumPorts(nullptr, 2, (LPBYTE)ports, bytesNeeded, &bytesNeeded, &portCount))
{
PORT_INFO_2 *portInfo = (PORT_INFO_2*)ports;
for(DWORD i = 0; i < portCount; ++i)
cout << portInfo[i].pPortName << endl;
}
delete [] ports;
I need to automatically connect to a specific device when my app is started, so I need to either get the port name for the bluetooth device in the first piece of code so I can connect to it, or check each portname in the second piece of code to make sure it's the right device before connecting to it.
How do I do it?
I remember struggling with this in the past.
the only solution i found was to use sockets for communicating with the Bluetooth device using its address, then use the send() and recv() methods for communicating with the device.
// assuming you have the BT device address in blueToothDeviceAddr;
char blueToothDeviceAddr[18];
SOCKET sock;
SOCKADDR_BTH sa = { 0,0,0,0 };
int sa_len = sizeof(sa);
// initialize windows sockets
WORD wVersionRequested;
WSADATA wsaData;
wVersionRequested = MAKEWORD( 2, 0 );
if( WSAStartup( wVersionRequested, &wsaData ) != 0 )
{
ExitProcess(100);
}
// parse the specified Bluetooth address
if( SOCKET_ERROR == WSAStringToAddress( blueToothDeviceAddr, AF_BTH,
NULL, (LPSOCKADDR) &sa, &sa_len ) )
{
ExitProcess(101);
}
// query it for the right port
// create the socket
sock = socket(AF_BTH, SOCK_STREAM, BTHPROTO_RFCOMM);
if( SOCKET_ERROR == sock )
{
ExitProcess(102);
}
// fill in the rest of the SOCKADDR_BTH struct
GUID pService = (GUID)SerialPortServiceClass_UUID;
SOCKADDR_BTH outSA;
sa.port = SDPGetPort(blueToothDeviceAddr, (LPGUID) &pService,&outSA);
if( sa.port == 0 )
{
ExitProcess(103);
}
// in case you have a pass code you need to register for authetication callback
// look the web for this part
// connect to the device
if( SOCKET_ERROR == connect( sock, (LPSOCKADDR) &outSA, sa_len ) )
{
int lastError = GetLastError();
ExitProcess(105);
}
Under the key:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\BTHENUM
you can find a subkey which has a list of keys containing the device address.
Under this last key, you can find a subkey named Device Parameters which finally has the PortName value.
The code is written in C++ with MFC libraries and is tested under Windows XP, 7 and 10. I hope it helps you !
// Returns the outgoing COM port of a bluetooth device given by address
int GetBluetoothCOM( CString sAddr )
{
int iPort = 0;
HKEY hKey_1;
DWORD KeyNdx_1 = 0;
DWORD MaxKeyLen_1;
char KeyNam_1[ MAX_PATH + 1 ];
LONG RetVal_1;
sAddr.MakeUpper();
sAddr.Replace( ":", "" );
sAddr.Replace( " ", "" );
// Enumerate keys under: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\BTHENUM
RegOpenKeyEx( HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Enum\\BTHENUM", NULL, KEY_READ | KEY_ENUMERATE_SUB_KEYS, &hKey_1 );
while( true )
{
MaxKeyLen_1 = MAX_PATH;
RetVal_1 = RegEnumKeyEx( hKey_1, KeyNdx_1, KeyNam_1, &MaxKeyLen_1, NULL, NULL, NULL, NULL );
if( RetVal_1 == ERROR_NO_MORE_ITEMS )
{
break;
}
if( RetVal_1 == ERROR_SUCCESS )
{
HKEY hKey_2;
DWORD KeyNdx_2 = 0;
DWORD MaxKeyLen_2;
char KeyNam_2[ MAX_PATH + 1 ];
LONG RetVal_2;
// Enumerate subkeys
RegOpenKeyEx( HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Enum\\BTHENUM\\" + CString( KeyNam_1 ), NULL, KEY_READ | KEY_ENUMERATE_SUB_KEYS, &hKey_2 );
while( true )
{
MaxKeyLen_2 = MAX_PATH;
RetVal_2 = RegEnumKeyEx( hKey_2, KeyNdx_2, KeyNam_2, &MaxKeyLen_2, NULL, NULL, NULL, NULL );
if( RetVal_2 == ERROR_NO_MORE_ITEMS )
{
break;
}
if( RetVal_2 == ERROR_SUCCESS )
{
// Find out if the key name contains &ADDRESS_
CString sKey = "SYSTEM\\CurrentControlSet\\Enum\\BTHENUM\\" + CString( KeyNam_1 ) + "\\" + CString( KeyNam_2 );
sKey.MakeUpper();
if( sKey.Find( "&" + sAddr + "_" ) != -1 )
{
HKEY hKey;
char szPort[ 100 + 1 ];
DWORD dwLen = 100;
// I find out the device
RegOpenKeyEx( HKEY_LOCAL_MACHINE, sKey + "\\Device Parameters", 0, KEY_READ, &hKey );
if( RegQueryValueEx( hKey, "PortName", NULL, NULL, ( LPBYTE ) &szPort, &dwLen ) == ERROR_SUCCESS )
{
szPort[ dwLen ] = 0;
CString sPort = CString( szPort );
sPort.MakeUpper();
if( sPort.Find( "COM" ) == -1 )
{
RegCloseKey( hKey );
continue;
}
sPort.Replace( "COM", "" );
sPort.Trim();
iPort = atoi( sPort.GetBuffer() );
if( iPort != 0 )
{
RegCloseKey( hKey );
break;
}
}
RegCloseKey( hKey );
}
}
++KeyNdx_2;
}
RegCloseKey( hKey_2 );
if( iPort != 0 )
{
break;
}
}
++KeyNdx_1;
};
RegCloseKey( hKey_1 );
return iPort;
}

SQLGetData issues using C++ and SQL Native Client

I have a C++ application that uses SQL Native Client to connect to a MS SQL Server 2000.
I am trying to retrieve a result from a TEXT column containing more data than the buffer initially allocated to it provides. To clarify my issue, I'll outline what I'm doing (code below):
allocate buffer of 1024 bytes use
bind buffer to column using SQLBindColumn
execute a SELECT query using SQLExecute
iterate through results using SQLFetch
SQLFetch was unable to return the entire result to my buffer: I'd like to use SQLGetData to retrieve the entire column value
The above order of operations presents a problem: SQLGetData does not work on bound columns in my driver.
A working solution is to use the SQL_DATA_AT_EXEC flag as illustrated by the code below.
Begin code:
#include <windows.h>
#include <sql.h>
#include <sqlext.h>
#include <sqltypes.h>
#include <sqlncli.h>
#include <cstdio>
#include <string>
const int MAX_CHAR = 1024;
bool test_retcode( RETCODE my_code, const char* my_message )
{
bool success = ( my_code == SQL_SUCCESS_WITH_INFO || my_code == SQL_SUCCESS );
if ( ! success )
{
printf( "%s", my_message );
}
return success;
}
int main ( )
{
SQLHENV EnvironmentHandle;
RETCODE retcode = SQLAllocHandle( SQL_HANDLE_ENV, SQL_NULL_HANDLE, &EnvironmentHandle );
test_retcode( retcode, "SQLAllocHandle(Env) failed!" );
retcode = SQLSetEnvAttr( EnvironmentHandle, SQL_ATTR_ODBC_VERSION, (SQLPOINTER) SQL_OV_ODBC3, SQL_IS_INTEGER );
test_retcode( retcode, "SQLSetEnvAttr(ODBC version) Failed" );
SQLHDBC ConnHandle;
retcode = SQLAllocHandle( SQL_HANDLE_DBC, EnvironmentHandle, &ConnHandle );
test_retcode( retcode, "Could not allocate MS SQL 2000 connection handle." );
SQLSMALLINT driver_out_length;
retcode = SQLDriverConnect( ConnHandle,
NULL, // we're not interested in spawning a window
(SQLCHAR*) "DRIVER={SQL Native Client};SERVER=localhost;UID=username;PWD=password;Database=Test;",
SQL_NTS,
NULL,
0,
&driver_out_length,
SQL_DRIVER_NOPROMPT );
test_retcode( retcode, "SQLConnect() Failed" );
SQLHSTMT StatementHandle;
retcode = SQLAllocHandle(SQL_HANDLE_STMT, ConnHandle, &StatementHandle);
test_retcode( retcode, "Failed to allocate SQL Statement handle." );
char* buffer = new char[ MAX_CHAR ];
SQLINTEGER length = MAX_CHAR - 1;
// -- Bind Block
retcode = SQLBindCol( StatementHandle,
1,
SQL_C_CHAR,
(SQLPOINTER) NULL,
(SQLINTEGER) SQL_DATA_AT_EXEC,
&length );
test_retcode( retcode, "Failed to bind column." );
// -- End Bind Block
retcode = SQLExecDirect( StatementHandle, (SQLCHAR*) "SELECT VeryLong FROM LongData", SQL_NTS );
test_retcode( retcode, "SQLExecDirect failed." );
// -- Fetch Block
retcode = SQLFetch( StatementHandle );
if ( retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO && retcode != SQL_NO_DATA )
{
printf( "Problem fetching row.\n" );
return 9;
}
printf( "Fetched data. length: %d\n", length );
// -- End Fetch Block
bool sql_success;
std::string data;
RETCODE r2;
do
{
r2 = SQLGetData( StatementHandle, 1, SQL_C_CHAR, buffer, MAX_CHAR, &length );
if ( sql_success = test_retcode( r2, "SQLGetData failed." ) )
{
data.append( buffer );
}
else
{
char* err_msg = new char[ MAX_CHAR ];
SQLSMALLINT req = 1;
SQLCHAR state[6];
SQLINTEGER error;
SQLINTEGER output_length;
int sql_state = SQLGetDiagRec( SQL_HANDLE_STMT, StatementHandle, req, state, &error, (SQLCHAR*) err_msg, (SQLINTEGER) MAX_CHAR, (SQLSMALLINT*) &output_length );
// state is: 07009, error_msg: "[Microsoft][SQL Native Client]Invalid Descriptor Index"
printf( "%s\n", err_msg );
delete err_msg;
return 9;
}
}
while ( sql_success && r2 != SQL_SUCCESS );
printf( "Done.\n" );
return 0;
}
Try to put SQLBindCol after SQLExecDirect.
For TEXT column use
retcode = SQLBindCol( StatementHandle, 1, SQL_C_CHAR,
(SQLPOINTER) NULL, (SQLINTEGER) SQL_DATA_AT_EXEC, &length );
in this way you can repeat SQLGetData to read entire TEXT data in multiple pieces