I'm attempting to connect to a MySQL server using C++ with the MySQL ODBC 5.1 Driver on Visual C++ 2008 Express Edition.
I'm following these instructions from MSDN:
The only difference is that I have to convert all the SQLCHAR
to SQLWCHAR
, to match the function params, hopefully that doesn't affect the connection string.
Every time I connect I get SQL_ERROR
as the return value.
So I'm assuming there's something wrong with the connection string or the connection statement.
I've tried
DNS=TestConnection; UID=user; PSW=password
and
SERVER=localhost; DRIVER={MySQL ODBC 5.1 Driver}; PORT=3306; UID=user; PSW=password; DATABASE=dbo;
and other similar connection strings.
The DNS that's called TestConnection
has the same info as the latter connection string.
The schema is dbo
, and have one table called testfire
with the following column specs:
TEST_ID( INT(11), PRIMARY, AUTO INCREMENT)
TEST_STRING( VARCHAR(50) )
TEST_INTEGER( INT(11) )
TEST_FLOAT( FLOAT )
TEST_DATE( DATETIME )
With 3 rows:
ID STRING INT FLOAT DATE
------------------------------------------------------
| 1 | Test 1 | 1 | 0.1 | 2001-01-01 00:00:00 |
| 2 | Test 2 | 2 | 0.2 | 2002-01-01 00:00:00 |
| 3 | Test 3 | 3 | 0.3 | 2003-01-01 00:00:00 |
------------------------------------------------------
I've attempted to retrieve the data using an Excel connection, mostly to see if the driver works. Excel successfully retrieved the data without problem, so the DNS named TestConnection is valid, and so are the credentials.
- What am I doing wrong?
- What should I change?
- Is it the conversion to
MYSQLWCHAR *
that messes up the connection string? - Is there a different, perhaps better and more efficient approach? (except perhaps class encapsulation, that's what I'm going to do after the test is successful)
Oh, and the compiler doesn't give any errors or warnings, the code is compiled and runs without any problems.
So, here's the test code, which returns "Query execution error":
#include <iostream>
#include <windows.h>
#include <sql.h>
#include <sqltypes.h>
#include <sqlext.h>
using namespace std;
int main(){
SQLHENV henv;
SQLHDBC hdbc;
SQLHSTMT hstmt;
SQLRETURN retcode;
HWND desktopHandle = GetDesktopWindow();
SQLWCHAR OutConnStr[255];
SQLSMALLINT OutConnStrLen;
SQLWCHAR szDNS[2048] ={0};
// 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, (void*)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);
// Connect to data source
retcode = SQLDriverConnect(
hdbc,
desktopHandle,
(SQLWCHAR*)"driver=MySQL Server",
_countof("driver=MySQL Server"),
OutConnStr,
255,
&OutConnStrLen,
SQL_DRIVER_PROMPT );
// Allocate statement handle
if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) {
retcode = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);
// Process data
retcode = SQLExecDirect(hstmt, (SQLWCHAR*)"SELECT TEST_STRING, TEST_INTEGER, TEST_FLOAT FROM dbo.testfire", SQL_NTS);
if (retcode == SQL_SUCCESS) {
SQLINTEGER sTestInt, cbTestStr, cbTestInt, cbTestFloat;
SQLFLOAT dTestFloat;
SQLCHAR szTestStr[200];
while (TRUE) {
cout<<"Inside loop";
retcode = SQLFetch(hstmt);
if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO) {
cout<<"An error occurred";
}
if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO){
SQLGetData(hstmt, 1, SQL_C_CHAR, szTestStr, 200, &cbTestStr);
SQLGetData(hstmt, 2, SQL_C_ULONG, &sTestInt, 0, &cbTestInt);
SQLGetData(hstmt, 3, SQL_C_FLOAT, &dTestFloat, 0,&cbTestFloat);
/* Print the row of data */
cout<<szTestStr<<endl;
cout<<sTestInt<<endl;
cout<<dTestFloat<<endl;
} else {
break;
}
}
}else{
cout<<"Query execution error."<<endl;
SQLWCHAR SqlState[6], Msg[SQL_MAX_MESSAGE_LENGTH];
SQLINTEGER NativeError;
SQLSMALLINT i, MsgLen;
SQLRETURN rc2;
// Get the status records.
i = 1;
while ((rc2 = SQLGetDiagRec(SQL_HANDLE_STMT, hstmt, i, SqlState, &NativeError,
Msg, sizeof(Msg), &MsgLen)) != SQL_NO_DATA) {
cout<<SqlState<<endl;
cout<<NativeError<<endl;
cout<<Msg<<endl;
cout<<MsgLen<<endl;
i++;
}
}
if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) {
SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
}
SQLDisconnect(hdbc);
}else{
cout<<"Connection error."<<endl;
}
SQLFreeHandle(SQL_HANDLE_DBC, hdbc);
}
}
SQLFreeHandle(SQL_HANDLE_ENV, henv);
}
system("pause");
return 0;
}
UPDATE
After updating the code (and post) using the correct arguments for the SQLDriverConnect
from the documentation provided by Mat (see comments below), the connection works. How can I do the same thing without having to prompt for the DNS name? Put window handle as null and...?
Now it fails at the SQLExecDirect(hstmt, (SQLWCHAR*)"SELECT TEST_STRING, TEST_INTEGER, TEST_FLOAT FROM dbo.testfire", SQL_NTS)
, but the query is correct, so, what's the problem?
The exact error message returned is:
Sql State: 42000
Native Error: 1064
Message:
Message Length: 211
42000: Syntax error or access violation
*StatementText contained an SQL statement that was not preparable or contained a syntax error.
The user did not have permission to execute the SQL statement contained in *StatementText.
So... what does that mean? How can I not have permission? How can that generate a syntax error, it's clearly a valid query?