SQLGetData issues using C++ and SQL Native Client - c++

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

Related

ODBC error 'String data, right truncation' when updating uniqueidentifier column with null value

I'm trying to update column of type uniqueidentifier with null. My query looks like:
UPDATE table_name SET column_name = ?
The column is bound with:
SQLLEN _nullLen(SQL_NULL_DATA);
_rc = SQLBindParameter(_hstmt,
static_cast<SQLUSMALLINT>(1),
SQL_PARAM_INPUT,
SQL_C_CHAR,
SQL_VARCHAR,
37,
NULL,
NULL,
0,
&_nullLen);
Executing the query results in a ODBC error 'String data, right truncation'.
Using the exact same SQLBindParameter I'm able to successfuly insert a new row with null data. Why does this not work for updating the row?
Please read
https://learn.microsoft.com/en-us/sql/odbc/reference/syntax/sqlbindparameter-function?view=sql-server-ver15 thoroughly.
According to it, the 6th parameter to SQLBindParameter is ColumnSize, which you set to 37. Why this value?
The 8th parameter is ParameterValuePtr, but you set it to NULL. Is NULL the value you are trying to set?
The 10th parameter is StrLen_or_IndPtr which you set to &_nullLen where SQLLEN _nullLen(SQL_NULL_DATA), but that's not the kind of thing it should point to.
Please make sure you understand each parameter passed to SQLBindParameter().
I suspect you update more than one column and that the truncation is not on the UNIQUEIDENTIFIER column but rather on something else.
I fired up my old VS and coded following sample program, and it updated to NULL just fine. You might wanna put a trace to see what was actually executed.
#include "stdafx.h"
#include <windows.h>
#include <sql.h>
#include <sqlext.h>
#include <stdio.h>
#include <conio.h>
#include <tchar.h>
#include <stdlib.h>
#include <sal.h>
#define TRYODBC(h, ht, x) { RETCODE rc = x;\
if (rc != SQL_SUCCESS) \
{ \
HandleDiagnosticRecord(h, ht, rc); \
} \
if (rc == SQL_ERROR) \
{ \
fwprintf(stderr, L"Error in " L#x L"\n"); \
goto Exit; \
} \
}
void HandleDiagnosticRecord(SQLHANDLE hHandle,
SQLSMALLINT hType,
RETCODE RetCode);
int __cdecl wmain(int argc, _In_reads_(argc) WCHAR **argv)
{
SQLHENV hEnv = NULL;
SQLHDBC hDbc = NULL;
SQLHSTMT hStmt = NULL;
WCHAR* pwszConnStr;
// Allocate an environment
if (SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &hEnv) == SQL_ERROR)
{
fwprintf(stderr, L"Unable to allocate an environment handle\n");
exit(-1);
}
TRYODBC(hEnv,
SQL_HANDLE_ENV,
SQLSetEnvAttr(hEnv,
SQL_ATTR_ODBC_VERSION,
(SQLPOINTER)SQL_OV_ODBC3,
0));
// Allocate a connection
TRYODBC(hEnv,
SQL_HANDLE_ENV,
SQLAllocHandle(SQL_HANDLE_DBC, hEnv, &hDbc));
pwszConnStr = L"";
TRYODBC(hDbc,
SQL_HANDLE_DBC,
SQLDriverConnect(hDbc,
GetDesktopWindow(),
pwszConnStr,
SQL_NTS,
NULL,
0,
NULL,
SQL_DRIVER_COMPLETE));
fwprintf(stderr, L"Connected!\n");
TRYODBC(hDbc,
SQL_HANDLE_DBC,
SQLAllocHandle(SQL_HANDLE_STMT, hDbc, &hStmt));
RETCODE RetCode = NULL;
SQLSMALLINT sNumResults;
//Here be dragons
SQLLEN _nullLen(SQL_NULL_DATA);
SQLRETURN _retcode = SQLBindParameter(hStmt, 1,
SQL_PARAM_INPUT,
SQL_C_CHAR,
SQL_VARCHAR,
37,
NULL,
NULL,
0,
&_nullLen);
if (_retcode == -1)
{
HandleDiagnosticRecord(hStmt, SQL_HANDLE_STMT, _retcode);
return 1;
}
_retcode = SQLPrepare(hStmt, L"UPDATE zz SET v = ? ", SQL_NTS);
if (_retcode == -1)
{
HandleDiagnosticRecord(hStmt, SQL_HANDLE_STMT, _retcode);
return 1;
}
RetCode= SQLExecute(hStmt);
switch (RetCode)
{
case SQL_SUCCESS_WITH_INFO:
{
HandleDiagnosticRecord(hStmt, SQL_HANDLE_STMT, RetCode);
// fall through
}
case SQL_SUCCESS:
{
// If this is a row-returning query, display
// results
TRYODBC(hStmt,
SQL_HANDLE_STMT,
SQLNumResultCols(hStmt, &sNumResults));
{
SQLLEN cRowCount;
TRYODBC(hStmt,
SQL_HANDLE_STMT,
SQLRowCount(hStmt, &cRowCount));
if (cRowCount >= 0)
{
wprintf(L"%Id %s affected\n",
cRowCount,
cRowCount == 1 ? L"row" : L"rows");
}
}
break;
}
case SQL_ERROR:
{
HandleDiagnosticRecord(hStmt, SQL_HANDLE_STMT, RetCode);
break;
}
default:
fwprintf(stderr, L"Unexpected return code %hd!\n", RetCode);
}
TRYODBC(hStmt,
SQL_HANDLE_STMT,
SQLFreeStmt(hStmt, SQL_CLOSE));
wprintf(L"Thanks for playing, type Enter to exit");
getchar();
Exit:
// Free ODBC handles and exit
if (hStmt)
{
SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
}
if (hDbc)
{
SQLDisconnect(hDbc);
SQLFreeHandle(SQL_HANDLE_DBC, hDbc);
}
if (hEnv)
{
SQLFreeHandle(SQL_HANDLE_ENV, hEnv);
}
wprintf(L"\nDisconnected.");
return 0;
}
void HandleDiagnosticRecord(SQLHANDLE hHandle,
SQLSMALLINT hType,
RETCODE RetCode)
{
SQLSMALLINT iRec = 0;
SQLINTEGER iError;
WCHAR wszMessage[1000];
WCHAR wszState[SQL_SQLSTATE_SIZE + 1];
if (RetCode == SQL_INVALID_HANDLE)
{
fwprintf(stderr, L"Invalid handle!\n");
return;
}
while (SQLGetDiagRec(hType,
hHandle,
++iRec,
wszState,
&iError,
wszMessage,
(SQLSMALLINT)(sizeof(wszMessage) / sizeof(WCHAR)),
(SQLSMALLINT *)NULL) == SQL_SUCCESS)
{
// Hide data truncated..
if (wcsncmp(wszState, L"01004", 5))
{
fwprintf(stderr, L"[%5.5s] %s (%d)\n", wszState, wszMessage, iError);
}
}
}

Is there a way to query .accdb/.mdb files with C++?

I have a school project where I need to develop an application that queries and writes into Access database files, but using C++.
After some research I found about ODBC, and that it could help me, but I had no luck. I've tried differents connection strings, but nothing seems to work.
What I've done so far:
#include "pch.h"
#include <windows.h>
#include <sqlext.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{
char szDSN[256] = "Driver={Microsoft Access Driver (*.mdb,
*.accdb)};DBQ=C:\\EntrySystem.mdb";
/* Data Access Method used in this sample */
const char* DAM = "Direct ODBC";
HENV hEnv;
HDBC hDbc;
/* ODBC API return status */
RETCODE rc;
int iConnStrLength2Ptr;
char szConnStrOut[256];
unsigned char query[] = "SELECT * from Condomino;";
SQLCHAR chval1[128], chval2[128], colName[128];
int ret1;
int ret2;
/* Number of rows and columns in result set */
SQLINTEGER rowCount = 0;
SQLSMALLINT fieldCount = 0, currentField = 0;
HSTMT hStmt;
/* Allocate an environment handle */
rc = SQLAllocEnv(&hEnv);
/* Allocate a connection handle */
rc = SQLAllocConnect(hEnv, &hDbc);
/* Connect to the 'Northwind 2007.accdb' database */
rc = SQLDriverConnect(hDbc, NULL, (SQLWCHAR*)szDSN,
SQL_NTS, (SQLWCHAR*)szConnStrOut,
255, (SQLSMALLINT*)&iConnStrLength2Ptr, SQL_DRIVER_NOPROMPT);
if (SQL_SUCCEEDED(rc))
{
printf("%s: Successfully connected to database. Data source name: \n %s\n",
DAM, szConnStrOut);
/* Prepare SQL query */
printf("%s: SQL query:\n %s\n", DAM, query);
rc = SQLAllocStmt(hDbc, &hStmt);
rc = SQLPrepare(hStmt, (SQLWCHAR*)query, SQL_NTS);
/* Bind result set columns to the local buffers */
rc = SQLBindCol(hStmt, 1, SQL_C_CHAR, chval1, 128, (SQLINTEGER*)&ret1);
rc = SQLBindCol(hStmt, 2, SQL_C_CHAR, chval2, 128, (SQLINTEGER*)&ret2);
/* Excecute the query and create a record set */
rc = SQLExecute(hStmt);
if (SQL_SUCCEEDED(rc))
{
printf("%s: Retrieve schema info for the given result set:\n", DAM);
SQLNumResultCols(hStmt, &fieldCount);
if (fieldCount > 0)
{
for (currentField = 1; currentField <= fieldCount; currentField++)
{
SQLDescribeCol(hStmt, currentField,
(SQLWCHAR*)colName, sizeof(colName), 0, 0, 0, 0, 0);
printf(" | %s", colName);
}
printf("\n");
}
else
{
printf("%s: Error: Number of fields in the result set is 0.\n", DAM);
}
printf("%s: Fetch the actual data:\n", DAM);
/* Loop through the rows in the result set */
rc = SQLFetch(hStmt);
while (SQL_SUCCEEDED(rc))
{
printf(" | %s | %s\n", chval1, chval2);
rc = SQLFetch(hStmt);
rowCount++;
};
printf("%s: Total Row Count: %d\n", DAM, rowCount);
rc = SQLFreeStmt(hStmt, SQL_DROP);
}
}
else
{
printf("%s: Couldn't connect to %s.\nLastError: %d\n", DAM, szDSN, GetLastError());
}
/* Disconnect and free up allocated handles */
SQLDisconnect(hDbc);
SQLFreeHandle(SQL_HANDLE_DBC, hDbc);
SQLFreeHandle(SQL_HANDLE_ENV, hEnv);
printf("%s: Cleanup. Done.\n", DAM);
return 0;
}
I expect it to query all rows from my table "condomino", but the library (sqlext) keep giving me the error "0". Any help would be welcome, if you any other solutions, let me know.
Yes.
Seems like you are having issue with Access connection strings. An alternative is to create User DNS ; go to control panel / administrative tools / ODBC Data Source / choose Microsoft Access Database - configure and set your path to your *.mdb ( use of *.accdb recommended)
Now your connection string will be simplified as ( driver name is case sensitive ):
SQLWCHAR outstr[1024];
SQLSMALLINT outstrlen;
SQLReturnCode = SQLDriverConnect(hDatabase, NULL, L"DSN=Microsoft Access Driver;", SQL_NTS,
outstr, sizeof(outstr), &outstrlen, SQL_DRIVER_COMPLETE);

How do I setup an ODBC connection to perform multiple querys(SQLExecDirect) in c++?

I have the following code (using ODBC to connect to an SQL database):
The connection is OK and also the first SQL_ExecuteQuery(), but the second and third SQL_ExecuteQuery() will return with an error (returncode -1 for SQLExecDirect).
I assume, that the "statement handle hstmt" will be overwritten after the first execution. But how can I avoid this? Thank you so much.
SQLHENV henv = SQL_NULL_HENV;
SQLHDBC hdbc = SQL_NULL_HDBC;
SQLHDBC hstmt= SQL_NULL_HSTMT;
SQLRETURN retcode = SQL_SUCCESS;
//Connect function
int SQL_Connect()
{
SQLWCHAR OutConnStr[255];
SQLSMALLINT OutConnStrLen;
// Allocate environment handle
retcode = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv);
// Set the ODBC version environment attribute
if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
{
retcode = SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER*)SQL_OV_ODBC3, 0);
// Allocate connection handle
if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
{
retcode = SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc);
// Set login timeout to 5 seconds
if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
{
SQLSetConnectAttr(hdbc, SQL_LOGIN_TIMEOUT, (SQLPOINTER)5, 0);
retcode = SQLDriverConnect( hdbc,
NULL,
#ifdef IPC
(SQLWCHAR *)L"DSN=TEST;Description=ODK;UID=FFF;PWD=XXX;Trusted_Connection=No;DATABASE=DDD;",
#else
(SQLWCHAR *)L"DSN=ODKSQL64;Description=ODK;UID=auto;PWD=Visu_KDbos;Trusted_Connection=No;DATABASE=Giesserei_BKO;",
#endif
SQL_NTS,
OutConnStr,
255,
&OutConnStrLen,
SQL_DRIVER_NOPROMPT);
// Allocate statement handle
if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
{
retcode = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);
cout<<"Verbindung OK"<<std::endl;
}
}
}
}
return retcode;
}
//Disonnect function
int SQL_Disconnect ()
{
SQLFreeHandle(SQL_HANDLE_STMT, hstmt );
SQLDisconnect(hdbc);
SQLFreeHandle(SQL_HANDLE_DBC, hdbc);
SQLFreeHandle(SQL_HANDLE_ENV, henv);
return 1;
}
//Query function
int SQL_ExecuteQuery()
{
short rc;
char material[50];
SQLINTEGER strlenmaterial;
//prepare query
std::wstring SQL_Statement = L"SELECT blablabla";
rc = SQLExecDirect(hstmt, const_cast<SQLWCHAR*>(SQL_Statement.c_str()), SQL_NTS);
if (rc==SQL_SUCCESS || rc==SQL_SUCCESS_WITH_INFO) {
SQLBindCol(hstmt, 1, SQL_C_CHAR, &material, (SQLINTEGER) sizeof(material), &strlenmaterial);
while (1) {
rc = SQLFetch(hstmt);
if (rc==SQL_SUCCESS || rc==SQL_SUCCESS_WITH_INFO) {
rc = 1;
}else {
break;
}
}
} else {
//no data found
rc = 3;
}
return rc;
}
int main()
{
short rc;
rc = SQL_Connect();
rc = SQL_ExecuteQuery();
rc = SQL_ExecuteQuery();
rc = SQL_ExecuteQuery();
rc = SQL_Disconnect();
return 0;
}
You can re-use a HSTMT handle, but before running a new query, you need to close the pending cursor. As you are binding the columns using SQLBindCol, you also need to unbind the columns, before binding them again.
In your SQL_ExecuteQuery(), before returning from the function call:
SQLFreeStmt(hstmt, SQL_UNBIND)
SQLFreeStmt(hstmt, SQL_CLOSE)
Now you are ready to execute another query, bind again and fetch the result.
Note that you could also change the logic of your program, and bind only once, and then skip the unbind-step: If you know that you are always interested in the result of the same column, you could bind the column before executing the query. You can then execute the query, read the result, call SQLFreeStmt with the SQL_CLOSE option and start over with executing the query.
See here for more details:
https://learn.microsoft.com/en-us/sql/odbc/reference/syntax/sqlfreestmt-function

How do I get a connection?

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 )
^^

bcp_init rturns access violation

I am trying to do bulk copy in sql server using odbc in c++.
here is my code:
#include <stdio.h>
#include <string.h>
#include <windows.h>
#include <sql.h>
#include <sqlext.h>
#include <odbcss.h>
#include<tchar.h>
SQLHENV henv = SQL_NULL_HENV;
HDBC hdbc1 = SQL_NULL_HDBC, hdbc2 = SQL_NULL_HDBC;
SQLHSTMT hstmt2 = SQL_NULL_HSTMT;
void Cleanup() {
if (hstmt2 != SQL_NULL_HSTMT)
SQLFreeHandle(SQL_HANDLE_STMT, hstmt2);
if (hdbc1 != SQL_NULL_HDBC) {
SQLDisconnect(hdbc1);
SQLFreeHandle(SQL_HANDLE_DBC, hdbc1);
}
if (hdbc2 != SQL_NULL_HDBC) {
SQLDisconnect(hdbc2);
SQLFreeHandle(SQL_HANDLE_DBC, hdbc2);
}
if (henv != SQL_NULL_HENV)
SQLFreeHandle(SQL_HANDLE_ENV, henv);
}
void HandleDiagnosticRecord (SQLHANDLE hHandle,
SQLSMALLINT hType,
RETCODE RetCode)
{
SQLSMALLINT iRec = 0;
SQLINTEGER iError;
WCHAR wszMessage[1000];
WCHAR wszState[SQL_SQLSTATE_SIZE+1];
if (RetCode == SQL_INVALID_HANDLE)
{
fwprintf(stderr, L"Invalid handle!\n");
return;
}
while (SQLGetDiagRec(hType,
hHandle,
++iRec,
wszState,
&iError,
wszMessage,
(SQLSMALLINT)(sizeof(wszMessage) / sizeof(WCHAR)),
(SQLSMALLINT *)NULL) == SQL_SUCCESS)
{
// Hide data truncated..
if (wcsncmp(wszState, L"01004", 5))
{
fwprintf(stderr, L"[%5.5s] %s (%d)\n", wszState, wszMessage, iError);
}
}
}
#define TRYODBC(h, ht, x) { RETCODE rc = x;\
if (rc != SQL_SUCCESS) \
{ \
HandleDiagnosticRecord (h, ht, rc); \
} \
if (rc == SQL_ERROR) \
{ \
fwprintf(stderr, L"Error in " L#x L"\n"); \
Sleep(30000); \
} \
}
void extract_error(
char *fn,
SQLHANDLE handle,
SQLSMALLINT type)
{
SQLINTEGER i = 0;
SQLINTEGER native;
SQLWCHAR state[ 7 ];
SQLWCHAR text[256];
SQLSMALLINT len;
SQLRETURN ret;
fprintf(stderr,
"\n"
"The driver reported the following diagnostics whilst running "
"%s\n\n",
fn);
do
{
ret = SQLGetDiagRec(type, handle, ++i, state, &native, text,
sizeof(text), &len );
if (SQL_SUCCEEDED(ret))
printf("%s:%ld:%ld:%s\n", state, i, native, text);
}
while( ret == SQL_SUCCESS );
}
int main() {
RETCODE retcode;
// BCP variables.
char *terminator = "\0";
// bcp_done takes a different format return code because it returns number of rows bulk copied
// after the last bcp_batch call.
DBINT cRowsDone = 0;
// Set up separate return code for bcp_sendrow so it is not using the same retcode as SQLFetch.
RETCODE SendRet;
// Allocate the ODBC environment and save handle.
retcode = SQLAllocHandle (SQL_HANDLE_ENV, NULL, &henv);
if ( (retcode != SQL_SUCCESS_WITH_INFO) && (retcode != SQL_SUCCESS)) {
printf("SQLAllocHandle(Env) Failed\n\n");
Cleanup();
return(9);
}
// Notify ODBC that this is an ODBC 3.0 app.
retcode = SQLSetEnvAttr(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();
return(9);
}
// Allocate ODBC connection handle, set bulk copy mode, and connect.
retcode = SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc1);
if ( (retcode != SQL_SUCCESS_WITH_INFO) && (retcode != SQL_SUCCESS)) {
printf("SQLAllocHandle(hdbc1) Failed\n\n");
Cleanup();
return(9);
}
retcode = SQLSetConnectAttr(hdbc1, SQL_COPT_SS_BCP, (void *)SQL_BCP_ON, SQL_IS_INTEGER);
if ( (retcode != SQL_SUCCESS_WITH_INFO) && (retcode != SQL_SUCCESS)) {
printf("SQLSetConnectAttr(hdbc1) Failed\n\n");
Cleanup();
return(9);
}
// sample uses Integrated Security, create the SQL Server DSN using Windows NT authentication
SQLWCHAR dsn[30] = L"mssqltest"; //Name DNS
SQLWCHAR user[10] = L"di_test";
SQLWCHAR pass[10] = L"di_test";
SQLWCHAR tb[20]=L"information1";
retcode = SQLConnectW(hdbc1, (SQLWCHAR *)dsn, SQL_NTS, (SQLWCHAR *) user, SQL_NTS, (SQLWCHAR *) pass, SQL_NTS);
if ( (retcode != SQL_SUCCESS) && (retcode != SQL_SUCCESS_WITH_INFO) ) {
printf("SQLConnect() Failed\n\n");
Cleanup();
return(9);
}
// TRYODBC(hdbc1, SQL_HANDLE_DBC, retcode);
// Initialize the bulk copy.
retcode = bcp_init(hdbc1,L"information1", NULL, NULL, DB_IN);
if ( (retcode != SUCCEED) ) {
printf("bcp_init(hdbc1) Failed\n\n");
Cleanup();
return(9);
}
//Define our array
SQLINTEGER custIDs[] = { 1, 2, 3, 4};
// Bind the program variables for the bulk copy.
retcode = bcp_bind(hdbc1, (BYTE *)custIDs[0], 4, SQL_VARLEN_DATA, NULL, (INT)NULL, SQLINT4, 2);
if ( (retcode != SUCCEED) ) {
printf("bcp_bind(hdbc1) Failed\n\n");
Cleanup();
return(9);
}
// Could normally use strlen to calculate the bcp_bind cbTerm parameter, but this terminator
// is a null byte (\0), which gives strlen a value of 0. Explicitly give cbTerm a value of 1.
retcode = bcp_bind(hdbc1, (BYTE *)custIDs[0], 4, SQL_VARLEN_DATA, NULL, (INT)NULL, SQLINT4, 3);
if ( (retcode != SUCCEED) ) {
printf("bcp_bind(hdbc1) Failed\n\n");
Cleanup();
return(9);
}
if ( (SendRet = bcp_sendrow(hdbc1) ) != SUCCEED ) {
printf("bcp_sendrow(hdbc1) Failed\n\n");
Cleanup();
return(9);
}
cRowsDone = bcp_done(hdbc1);
if ( (cRowsDone == -1) ) {
printf("bcp_done(hdbc1) Failed\n\n");
Cleanup();
return(9);
}
printf("Number of rows bulk copied after last bcp_batch call = %d.\n", cRowsDone);
// Cleanup.
SQLFreeHandle(SQL_HANDLE_STMT, hstmt2);
SQLDisconnect(hdbc1);
SQLFreeHandle(SQL_HANDLE_DBC, hdbc1);
SQLDisconnect(hdbc2);
SQLFreeHandle(SQL_HANDLE_DBC, hdbc2);
SQLFreeHandle(SQL_HANDLE_ENV, henv);
}
in the line:
retcode = bcp_init(hdbc1,L"information1", NULL, NULL, DB_IN);
I get an exception which says, access violation. I recieve no error but an exception.
Does anyone know how should I solve this problem?
I've been using bcp_xxxx functions since 12-15 years in one win32 program. That program was compiled on VS6 and is still on production today.
I recently rewrote the project on VS2015 (finally…). And I had issues with bcp_xxxx functions like you too.
The original VS6 program had these files included:
#include <sql.h>
#include <sqlext.h>
#include "C:\Program Files\Microsoft SQL Server\80\Tools\DevTools\Include\odbcss.h"
SQLDriverConnect() function was used with a connection string containing Driver=SQL Server.
The odbcbcp.lib was added to the linker libraries list. No issues.
The program has been completely revisited for VS2015, changing when appropriate ODBC functions to their version 3.x, and allowing 32 or 64 bit binaries with UNICODE or ANSI charsets.
Recent documentation suggests to use sqlncli.h header and sqlncli11.lib when using bcp_xxxx functions:
#include <sql.h>
#include <sqlext.h>
#define _SQLNCLI_ODBC_
#ifdef _WIN64
#include "C:\Program Files\Microsoft SQL Server\110\SDK\Include\sqlncli.h"
#else
#include "C:\Program Files (x86)\Microsoft SQL Server\110\SDK\Include\sqlncli.h"
#endif
I did it like suggested, and... crash.
I then linked back to odbcbcp.lib, and it works like a charm.
So, I started to search how to use sqlncli11.lib and I found the ODBC connection string should contain Driver=SQL Server Native Client 11.0.
In brief:
- (1) Driver=SQL Server with odbcbcp.lib works
- (2) Driver=SQL Server with sqlncli11.lib makes bcp_init() to crash
- (3) Driver=SQL Server Native Client 11.0 with odbcbcp.lib makes bcp_init() to fail (it returns FAIL=0)
- (4) Driver=SQL Server Native Client 11.0 with sqlncli11.lib works.
Options (1) and (4) are working.
But I found too that using option (4) allows queries to run significantly faster !
So I’ll keep that last one.
Hope this helps.