Im trying to Connect to a SQL-Server in C++, using User/PW Auth and not windows auth.
First, when i try to connect to the same Server in Excel (also adding user/pw there) it first asks me for a not secure connection, but manages to connect.
Now when i want to connect to the same Server with c++, it gives me following errors:
Could not connect to SQL Server
SqlState: 28000
Code: 18456
Msg: [Microsoft][SQL Server Native Client 11.0][SQL Server]Login failed for user 'domain\user'.
So im really sure whats wrong.
The Rest of the Code looks like this. Alloc Handles
if (SQL_SUCCESS != SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &sqlEnvHandle)) {
std::cout << "Error in SQLAllocHandle of sqlEnvHandle" << std::endl;
return false;
}
if (SQL_SUCCESS != SQLSetEnvAttr(sqlEnvHandle, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3, 0)) {
std::cout << "Error in SQLSetEnvAttr of sqlEnvHandle" << std::endl;
return false;
}
if (SQL_SUCCESS != SQLAllocHandle(SQL_HANDLE_DBC, sqlEnvHandle, &sqlConnHandle)) {
std::cout << "Error in SQLAllocHandle of sqlConnHandle" << std::endl;
return false;
}
The ConnectionString
std::wstring ConnectionString = L"DRIVER=" + DriverName + L";SERVER=" + ServerName + L", " + ServerPort + L";DATABASE=" + DatabaseName + L";UID=" + LoginUser + L";PWD=" + LoginPassword + L";";
and the SQLDriverConnect itself, witch fails with SQL_ERROR
SQLDriverConnect(sqlConnHandle,
NULL,
(SQLWCHAR*)ConnectionString.c_str(),
SQL_NTS,
retconstring,
1024,
NULL,
SQL_DRIVER_NOPROMPT)
I hope someone cal help me/tell me why Excel manages a Connection but im Failing in C++?
Related
In short very new to all of this, so I'm not even sure if this is possible,anyway, I need to be able to make code, that will stand between programs like power bi and database. For example Power bi should be able to use my custom driver to connect to any database and when I he request data from database I need to mask all of the data before power bi receive it. So far the only guide I got was to look for custom ODBC and how to implement that. Not even sure how all of those work.
My understanding is that I need to make some c++ library, that implements some ODBC functions,API and then I need to through registry register this dll and power bi should then be able to see my driver.
My question is how would I start doing that, is there some examples code, that do just the minimal stuff, so I can see the concept on how I should do that?
EDIT:
I made some progress and now I have concrete question.
I made data source (DSN) on windows, linked with postgres driver and now I'm trying to connect to that dsn through c++ code.
But I m having trouble with that.
This is my code
enter code here
HENV hEnv = NULL; // for allocating memory usingSQLAllocEnv
HDBC hDBC = NULL; // connection handler
HSTMT hStmt = NULL; // statement handler
const char* szDSN = "PostgreSQL30"; // DataSourceName (config in windows
control panel)
const char* szUID = "postgres"; //username of the database
const char* szPasswd = "postgres"; //password of the database
RETCODE retcode;
int rcod = 1;
int i, j, no = 2;
int main()
{
retcode = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &hEnv);
std::cout << retcode << std::endl;
SQLSetEnvAttr(hEnv, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3, 0);
retcode = SQLAllocHandle(SQL_HANDLE_DBC, hEnv, &hDBC);
std::cout << retcode << std::endl;
retcode = SQLConnectA(hDBC,(SQLCHAR*)szDSN, SQL_NTS, (SQLCHAR*)szUID, SQL_NTS, (SQLCHAR*)szPasswd, SQL_NTS);
std::cout << (SQLCHAR*)szUID << std::endl;
if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
{
// connected!!
std::cout << "Hello World!\n SUCCESSS COME ON PLEASE";
}
else {
std::cout << retcode << std::endl;
}
SQLFreeHandle(SQL_HANDLE_ENV, &hEnv);
SQLFreeHandle(SQL_HANDLE_DBC, &hDBC);
}
But SQLConnect returns SQL_ERROR.
Question is how would I connect to a data source name with c++ code ?
I'm trying to restart the "postgres" service in my C++ application on windows 10 but I having some problems with access rights( my guess ). I have tried running the code as a domain user that has local admin rights and as a local administrator but none of these seem to work. I can restart the service manually via the services.msc. The code fails already at OpenSCManager which returns NULL. I have also tried other access rights but then OpenService fails. This is my code
auto showError = []()
{
std::ostringstream os;
os << GetLastError();
qDebug() << "Restart PostgreSQL service failed : " << QString::fromStdString( os.str());
};
SERVICE_STATUS Status;
SC_HANDLE SCManager = OpenSCManager(NULL, SERVICES_ACTIVE_DATABASE, SC_MANAGER_ALL_ACCESS );
if(SCManager == NULL)
showError();
SC_HANDLE SHandle = OpenService(SCManager, L"postgres", SERVICE_ALL_ACCESS );
if(SHandle == NULL)
showError();
if(!ControlService(SHandle, SERVICE_CONTROL_STOP, &Status))
showError();
do
{
QueryServiceStatus(SHandle, &Status);
qDebug() << "Checking Service Status...\n";
}while(Status.dwCurrentState != SERVICE_STOPPED);
if(!StartService(SHandle, 0, NULL))
showError();
std::cin.sync();
std::cin.ignore();
CloseServiceHandle(SCManager);
CloseServiceHandle(SHandle);
You are asking for too many permissions that you don't actually need. Don't use SC_MANAGER_ALL_ACCESS and SERVICE_ALL_ACCESS. NEVER ask for more permissions than you really need. All you really need in this situation are SC_MANAGER_CONNECT for OpenSCManager(), and SERVICE_STOP, SERVICE_START and SERVICE_QUERY_STATUS for OpenService().
If you still get an "Access Denied" error after fixing this in your code, then the service really requires your account to have permissions to start/stop the service. So either run your code in an elevated admin process, or at least configure your user account with the appropriate permissions.
And FYI, your query loop is too minimalistic. You are ignoring the Status that ControlService() reports, and you are not accounting for the possibility of the service entering a pending state, or refusing to stop. You need to check the initial status, only if it is in a pending state than enter the query loop until it is no longer in a pending state, then check the final state for stopped before then attempting to start the service. Also be sure to check if the service hangs while trying to stop it.
See Stopping a Service on MSDN.
Try something more like this:
auto showError = []()
{
DWORD err = GetLastError();
qDebug() << "Restart PostgreSQL service failed. Error: " << err << "\n";
};
SERVICE_STATUS Status;
SC_HANDLE SCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
if (!SCManager)
{
showError();
return;
}
SC_HANDLE SHandle = OpenService(SCManager, L"postgres", SERVICE_STOP | SERVICE_START | SERVICE_QUERY_STATUS);
if (!SHandle)
{
showError();
CloseServiceHandle(SCManager);
return;
}
if (!QueryServiceStatus(SHandle, &Status))
{
showError();
CloseServiceHandle(SHandle);
CloseServiceHandle(SCManager);
return;
}
if (Status.dwCurrentState != SERVICE_STOPPED)
{
qDebug() << "Stopping PostgreSQL service...\n";
if (!ControlService(SHandle, SERVICE_CONTROL_STOP, &Status))
{
showError();
CloseServiceHandle(SHandle);
CloseServiceHandle(SCManager);
return;
}
DWORD dwStartTime = GetTickCount();
DWORD dwTimeout = 30000; // 30-second time-out
DWORD dwWaitTime;
while (Status.dwCurrentState == SERVICE_STOP_PENDING)
{
qDebug() << "Waiting for PostgreSQL service to stop...\n";
dwWaitTime = Status.dwWaitHint / 10;
if (dwWaitTime < 1000)
dwWaitTime = 1000;
else if (dwWaitTime > 10000)
dwWaitTime = 10000;
Sleep(dwWaitTime);
if (!QueryServiceStatus(SHandle, &Status))
{
showError();
CloseServiceHandle(SHandle);
CloseServiceHandle(SCManager);
return;
}
if (Status.dwCurrentState != SERVICE_STOP_PENDING)
break;
if (GetTickCount() - dwStartTime > dwTimeout)
{
qDebug() << "Stop of PostgreSQL service timed out.\n";
CloseServiceHandle(SHandle);
CloseServiceHandle(SCManager);
return;
}
}
if (Status.dwCurrentState != SERVICE_STOPPED)
{
qDebug() << "Restart PostgreSQL service failed. Service did not stop.\n";
CloseServiceHandle(SHandle);
CloseServiceHandle(SCManager);
return;
}
qDebug() << "PostgreSQL service stopped successfully.\n";
}
if (!StartService(SHandle, 0, NULL))
showError();
CloseServiceHandle(schService);
CloseServiceHandle(schSCManager);
I'm using Microsoft's ODBC driver to connect a C++/Linux application to a SQL Server database running remotely, and when I try to connect to the database, the call fails with SQL_INVALID_HANDLE. Reading through their documentation, I find this:
SQL_INVALID_HANDLE Function failed due to an invalid environment, connection, statement, or descriptor handle. This indicates a programming error. No additional information is available from SQLGetDiagRec or SQLGetDiagField. This code is returned only when the handle is a null pointer or is the wrong type, such as when a statement handle is passed for an argument that requires a connection handle.
Fair enough, but at no point in the creation of the handles and environment prior to the connect statement do I get any errors. Also, for the second argument, their documentation says I can pass in a null pointer if there is no desktop window (as is the case on this linux console application). Here is a MVCE, adapted from Microsoft's example program:
#include "sql.h"
#include "sqlext.h"
#include "msodbcsql.h"
#include <iostream>
#include <string>
int main(int, char**)
{
using std::cerr;
using std::endl;
SQLHENV henv;
SQLHDBC hdbc;
HWND dhandle = nullptr; // no desktop handle in linux
SQLHSTMT hstmt;
SQLRETURN retcode;
SQLCHAR OutConnStr[255];
SQLSMALLINT OutConnStrLen;
retcode = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv);
if (!(retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO))
cerr << "SQLAllocHandle (environment) failed " << retcode << endl;
retcode = SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER*)SQL_OV_ODBC3, 0);
if (!(retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO))
cerr << "SQLSetEnvAttr failed " << retcode << endl;
retcode = SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc);
if (!(retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO))
cerr << "SQLAllocHandle (connection) failed " << retcode << endl;
retcode = SQLSetConnectAttr(hdbc, SQL_LOGIN_TIMEOUT, (SQLPOINTER)5, 0);
if (!(retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO))
cerr << "SQLSetConnectAttr failed " << retcode << endl;
std::string dsn = "DRIVER={ODBC Driver 17 for SQL Server};SERVER=*.*.*,1433;DATABASE=***;UID=***;PWD=***";
retcode = SQLDriverConnect(hdbc, dhandle, (SQLCHAR*)dsn.c_str(), dsn.length(), OutConnStr, 255, &OutConnStrLen, SQL_DRIVER_PROMPT);
if (!(retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO))
cerr << "SQLDriverConnect failed " << retcode << endl;
// cleanup code redacted for brevity
return 0;
}
The program outputs SQLDriverConnect failed -2, which is SQL_INVALID_HANDLE. I'm stumped. hdbc is clearly the right type, and examining it in the debugger shows me it is not null.
It may be worth noting that the exact same connection string works in a python program using pyodbc. It seems that the C++ program isn't even getting as far as looking at that string, though. It just doesn't like the handle I'm sending into the connect call.
Microsoft's documentation clearly says they provide no additional information. If anyone can provide any direction on how to diagnose/debug this, I'd appreciate it greatly.
This application uses gcc 4.9.1 on Centos 7.
After two weeks of digging, this turned out to be some kind of versioning problem.
Eventually, this program will be doing some BCP uploads via Microsoft's extensions in libmsodbcsql.so. It turns out that library also has implementations of many of the SQL* functions, which are the ones that are failing in this test program. When I change the order of the link so that libodbc.so is before the MSFT extensions library so that the loader finds those implementations first, the program works fine.
I'm curious why this is, and it probably points to something else I'm doing wrong that may bite me down the road. But for now, at least, I am able to get connected to the database and do basic queries and updates.
Thanks to those who helped.
In Unix does the handle not have to be a non-null value for dialogs to be displayed.
For any handle in SQL Server, it has to be allocated before used!
So the order is Environment, Connection and Statement.
Example:
SQLHENV hEnv = nullptr;
SQLHDBC hDbc = nullptr;
SQLHSTMT hStmt = NULL;
Allocations
SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &hEnv);
SQLAllocHandle(SQL_HANDLE_DBC, hEnv, &hDbc);
SQLAllocHandle(SQL_HANDLE_STMT, hDbc, &hStmt);
Below is sample code that may help you.
Basics fast,
Create table in your sql server database and insert some data
create table test (id int, name nvarchar(128));
Insert some data
insert into test (id,name) values (1, 'Awesome Name');
C++ Code to query items in the table
#include <iostream>
#include <string>
#include <sql.h>
#include <sqlext.h>
int main(int argc, char **argv) {
SQLHENV hEnv = nullptr;
SQLHDBC hDbc = nullptr;
SQLHSTMT hStmt = NULL;
/**
* Allocate environment handle
*/
SQLRETURN allocReturn = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &hEnv);
//Set environment
SQLRETURN setEnvReturn = SQLSetEnvAttr(hEnv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER) SQL_OV_ODBC3, 0);
//Allocate connection handle
SQLAllocHandle(SQL_HANDLE_DBC, hEnv, &hDbc);
SQLCHAR *connection_string = (SQLCHAR *)
"DRIVER={ODBC Driver 17 for SQL Server};SERVER=localhost,1433;DATABASE=database;UID=sa;PWD=password";
//Connect to database
SQLRETURN connReturn = SQLDriverConnect(hDbc, NULL, connection_string, SQL_NTS, NULL, 0, NULL,
SQL_DRIVER_COMPLETE);
//Allocate Statement Handle
SQLAllocHandle(SQL_HANDLE_STMT, hDbc, &hStmt);
//Create statement
SQLCHAR *query = (SQLCHAR *) "SELECT * FROM TEST;";
SQLRETURN sqlPrepareResponse = SQLPrepare(hStmt, query, SQL_NTS); //strlen(reinterpret_cast<const char *>(query))
//Bind columns
SQLCHAR personName[20];
SQLLEN personNameIndex;
SQLRETURN bindNameResponse = SQLBindCol(hStmt, 2, SQL_C_CHAR, personName, sizeof(personName),
&personNameIndex);
SQLINTEGER personId;
SQLLEN personIdIndex;
SQLRETURN personIdBindResponse = SQLBindCol(hStmt, 1, SQL_INTEGER, &personId, 0, &personIdIndex);
SQLRETURN execResponse = SQLExecute(hStmt);
SQLRETURN fetchResponse;
while ((fetchResponse = SQLFetch(hStmt)) != SQL_NO_DATA) {
std::cout << "ID: [" << personId << "] :" << personName << std::endl;
}
/* Free the statement handle. */
SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
/* Disconnect from the database. */
SQLDisconnect(hDbc);
/* Free the connection handle. */
SQLFreeHandle(SQL_HANDLE_DBC, hDbc);
/* Free the environment handle. */
SQLFreeHandle(SQL_HANDLE_ENV, hEnv);
return EXIT_SUCCESS;
}
I have exactly the same error using similar code (that was working in Ubuntu 18.04, but not with a update to 20.04)
cat /etc/odbcinst.ini
[ODBC Driver 17 for SQL Server]
Description=Microsoft ODBC Driver 17 for SQL Server
Driver=/opt/microsoft/msodbcsql17/lib64/libmsodbcsql-17.5.so.2.1
UsageCount=1
using this connection string
DRIVER=ODBC Driver 17 for SQL Server;SERVER=127.0.0.1, 1433;UID=SA;PWD=password;DATABASE=my_database;
this is my library link order
if(UNIX)
find_program(LSB_RELEASE_EXEC lsb_release)
execute_process(COMMAND ${LSB_RELEASE_EXEC} -is OUTPUT_VARIABLE LSB_RELEASE_ID_SHORT OUTPUT_STRIP_TRAILING_WHITESPACE)
message(STATUS "Building in " ${LSB_RELEASE_ID_SHORT})
if("${LSB_RELEASE_ID_SHORT}" STREQUAL "Ubuntu")
message(STATUS "Linking with SQL-Server library")
set(lib_dep ${lib_dep} msodbcsql-17)
endif()
set(lib_dep ${lib_dep} pthread odbc dl)
endif()
As noted in the solution above, changing the link order fixed the problem
set(lib_dep ${lib_dep} pthread odbc dl msodbcsql-17)
Can I use an ODBC statement handle again (i.e. is it valid) after a command using it, for example SQLExecute, fails? (does not return SQL_SUCCESS or SQL_SUCCESS_WITH_INFO)
And is this possibly DBMS/driver-specific?
I couldn't find anything about this on the ODBC Programmer's reference page.
I do not find any authoritative answers in the doc neither. But I would say: Yes - you can, except the error code returned is SQL_INVALID_HANDLE:
Reasoning:
None of the docs of the functions that require a statement-handle as argument mention anything about a handle being invalidated in case of error. All that matters is the return code. So if its not explicitly forbidden, it should work.
In the case you get an SQL_ERROR returned, you can use the same statement handle to fetch more information about that error. So the statement handle still has a valid context.
3: We are using the same statement again and again, even in case of SQL_ERROR returned. And we did not have any problems so far. Well, but mostly we do not get any Errors..
Update, after comment about "Statement has been terminated": Yes, you can re-use the same statement-handle. The error is just indicating that the currently running statement has been terminated by the server. See the following sample, which produces such an error and then uses the same statement again to do a successful insert:
#include <windows.h>
#include <tchar.h>
#include <iostream>
#include <sql.h>
#include <sqlext.h>
#include <sqlucode.h>
void printErr(SQLHANDLE handle, SQLSMALLINT handleType)
{
SQLSMALLINT recNr = 1;
SQLRETURN ret = SQL_SUCCESS;
while (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO)
{
SQLWCHAR errMsg[SQL_MAX_MESSAGE_LENGTH + 1];
SQLWCHAR sqlState[5 + 1];
errMsg[0] = 0;
SQLINTEGER nativeError;
SQLSMALLINT cb = 0;
ret = SQLGetDiagRec(handleType, handle, recNr, sqlState, &nativeError, errMsg, SQL_MAX_MESSAGE_LENGTH + 1, &cb);
if (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO)
{
std::wcerr << L"ERROR; native: " << nativeError << L"; state: " << sqlState << L"; msg: " << errMsg << std::endl;
}
++recNr;
}
}
int _tmain(int argc, _TCHAR* argv[])
{
// connect to db
SQLRETURN nResult = 0;
SQLHANDLE handleEnv = 0;
nResult = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, (SQLHANDLE*)&handleEnv);
nResult = SQLSetEnvAttr(handleEnv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3_80, SQL_IS_INTEGER);
SQLHANDLE handleDBC = 0;
nResult = SQLAllocHandle(SQL_HANDLE_DBC, handleEnv, (SQLHANDLE*)&handleDBC);
SQLWCHAR strConnect[256] = L"Driver={SQL Server};Server=.\\INSTANCE;Database=Test;Trusted_Connection=yes;";
SQLWCHAR strConnectOut[1024] = { 0 };
SQLSMALLINT nNumOut = 0;
nResult = SQLDriverConnect(handleDBC, NULL, (SQLWCHAR*)strConnect, SQL_NTS, (SQLWCHAR*)strConnectOut, sizeof(strConnectOut), &nNumOut, SQL_DRIVER_NOPROMPT);
if (!SQL_SUCCEEDED(nResult))
printErr(handleDBC, SQL_HANDLE_DBC);
SQLHSTMT handleStatement = SQL_NULL_HSTMT;
nResult = SQLAllocHandle(SQL_HANDLE_STMT, handleDBC, (SQLHANDLE*)&handleStatement);
if (!SQL_SUCCEEDED(nResult))
printErr(handleDBC, SQL_HANDLE_DBC);
// try to drop table Wallet, ignore if it exists
nResult = SQLExecDirect(handleStatement, L"DROP TABLE Wallet", SQL_NTS);
// create table Wallet
nResult = SQLExecDirect(handleStatement, L"CREATE TABLE Wallet (WalletID int NOT NULL, Name nvarchar(5) NOT NULL)", SQL_NTS);
if (!SQL_SUCCEEDED(nResult))
printErr(handleStatement, SQL_HANDLE_STMT);
// Create a query that fails with data truncation and statement got terminated error:
nResult = SQLExecDirect(handleStatement, L"INSERT INTO Wallet (WalletID, Name) VALUES (1, 'SomethingTooLong')", SQL_NTS);
if (!SQL_SUCCEEDED(nResult))
printErr(handleStatement, SQL_HANDLE_STMT);
// and now run a query on the same statement and check in the db: Has been inserted just fine
nResult = SQLExecDirect(handleStatement, L"INSERT INTO Wallet (WalletID, Name) VALUES (2, 'Fan')", SQL_NTS);
if (!SQL_SUCCEEDED(nResult))
printErr(handleStatement, SQL_HANDLE_STMT);
// actually we should now free all handles properly...
return 0;
}
The output of this program is:
ERROR; native: 8152; state: 22001; msg: [Microsoft][ODBC SQL Server
Driver][SQL Server]String or binary data would be truncated. ERROR;
native: 3621; state: 01000; msg: [Microsoft][ODBC SQL Server
Driver][SQL Server]The statement has been terminated.
But the last insert query that works without error, using the statement, has been executed successfully: Check in your database and see that the row has been inserted (and that the SQLExecDirect did not return any error).
See here for SQL_INVALID_HANDLE: https://msdn.microsoft.com/en-us/library/ms716219(v=vs.85).aspx
Greeting guys , I have been looking on the internet on how to connect C++ code with SQLEXPRESS DATABASE, I have read some threads about how to connect but yet... I CAN NOT follow any of them... neither they are working for me.
I dont want to use MFC or any kind of C++ methods for DB , I want to do it via raw coding.
problem:
the Database connectable via Visual studio and via Microsoft SQL
Server Studio , I can add or drop tables as I LIKE, I believe the DB
as it is, is working fine
the C++ code is here " I am Connecting to a local DB "
I got this code from one of the threads and was not able to figure out how to do the SQLDriverConnect,
I am very noob in C++... adding SQLEXPRESS on top of that...
edit:
I dont know what that 3055 in the connect code is...
thank you in advance for help
Data base information:
UDL:
this what I got from the udl
( Provider=SQLOLEDB.1;Integrated Security=SSPI;
Persist Security Info=False;User ID=sa;Initial Catalog=Holpa;Data Source=AMH )
Microsoft server:
Server type: Database Engine
Server name: AMH
Authen: SQL server Authen
Login: sa
password : amh999
VisualStudio:
Data Source=AMH;Initial Catalog=Holpa;User ID=sa;Password=***********
.NET Framework Data Provider for SQL Server
Open
Microsoft SQL Server
Owner sa
running on local machine.
#include <iostream>
#include <windows.h>
#include <sqltypes.h>
#include <sql.h>
#include <sqlext.h>
using namespace std;
SQLHANDLE sqlenvhandle = SQL_NULL_HANDLE;
SQLHANDLE sqlconnectionhandle = SQL_NULL_HANDLE;
SQLHANDLE sqlstatementhandle = SQL_NULL_HANDLE;
SQLRETURN retcode;
void show_error(RETCODE rc, SQLHENV hEnv, SQLHDBC hDbc,
SQLHSTMT hStmt, const char *action)
{
SQLWCHAR szMessage[256];
SQLWCHAR szState[6];
SDWORD sdwNative;
SWORD swMsgLen;
SQLError(hEnv, hDbc, hStmt, szState, &sdwNative, szMessage,
sizeof(szMessage), &swMsgLen);
wcout << "wcout MESSAGE: " << szMessage << "\n SQLSTATE " << szState << endl;
printf("Error %d performing %s\n SQLState=%s\nSQL message=%s\n",rc, action, szState, szMessage);
}
void CloseSQL()
{
SQLFreeHandle(SQL_HANDLE_STMT, sqlstatementhandle);
SQLDisconnect(sqlconnectionhandle);
SQLFreeHandle(SQL_HANDLE_DBC, sqlconnectionhandle);
SQLFreeHandle(SQL_HANDLE_ENV, sqlenvhandle);
}
int main()
{
if (SQL_SUCCESS != SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &sqlenvhandle))
{
printf("huh \n");
CloseSQL();
goto END;
}
if (SQL_SUCCESS != SQLSetEnvAttr(sqlenvhandle, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3, 0))
{
printf("huh \n");
CloseSQL();
goto END;
}
if (SQL_SUCCESS != SQLAllocHandle(SQL_HANDLE_DBC, sqlenvhandle, &sqlconnectionhandle))
{
printf("huh \n");
CloseSQL();
goto END;
}
printf("Driver Initialised\n");
SQLWCHAR retconstring[1024];
printf("about to Driver Conneect\n");
retcode = SQLDriverConnect(sqlconnectionhandle,
NULL,
(SQLWCHAR*)"DRIVER={SQL Server};SERVER=AMH, 3055;DATABASE=Holpa;UID=sa;PWD=amh999;",
SQL_NTS,
retconstring,
1024,
NULL,
SQL_DRIVER_NOPROMPT);
if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
{
printf("Connection made\n");
}
else
{
show_error(retcode, sqlenvhandle, sqlconnectionhandle, sqlstatmenthandle, "Connecting.");
}
END:
printf("\n");
printf("Program End, press enter key to exit!");
getchar(); // waits for input
return 0;
}
ERRORS:
the error codes are not constants... they keep changing ... example I got the following:
Message: 0022E1EC
SQLSTATE: 0022e9f4
re-run the program
Message: 009fdc4c
SQLSTATE: 009fe454
rerun the code
Message: 00aadd90
SQLSTATE: 00aae598
Firstly, you need a diagonostic function that works:
void show_error(RETCODE rc, SQLHENV hEnv, SQLHDBC hDbc,
SQLHSTMT hStmt, const char *action)
{
char szMessage[256];
char szState[6];
SDWORD sdwNative;
SWORD swMsgLen;
SQLError(hEnv, hDbc, hStmt, szState, &sdwNative, szMessage,
sizeof(szMessage), &swMsgLen);
printf("Error %d performing %s\n"
"SQLState=%s\nSQL message=%s\n",
rc, action, szState, szMessage);
}
Then call it if anything goes wrong, e.g.:
if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
{
printf("Connection made\n");
}
else
{
show_error(retcode, sqlenvhandle, sqlconnectionhandle, sqlstatmenthandle, "Connecting.");
}