Подключение к серверу MySQL с использованием C ++ - PullRequest
4 голосов
/ 22 октября 2011

Я пытаюсь подключиться к серверу MySQL, используя C ++ с драйвером MySQL ODBC 5.1 в Visual C ++ 2008 Express Edition.

Я следую этим инструкциям MSDN:

Единственное отличие состоит в том, что мне нужно преобразовать все SQLCHAR в SQLWCHAR, чтобы соответствовать параметрам функции, надеюсь, это не повлияет на строку подключения.

Каждый раз, когда я подключаюсь, я получаю SQL_ERROR в качестве возвращаемого значения. Поэтому я предполагаю, что что-то не так со строкой соединения или оператором соединения.

Я пробовал

DNS=TestConnection; UID=user; PSW=password

и

SERVER=localhost; DRIVER={MySQL ODBC 5.1 Driver}; PORT=3306; UID=user; PSW=password; DATABASE=dbo; и другие похожие строки подключения.

DNS, который называется TestConnection, имеет ту же информацию, что и последняя строка подключения.

Схема dbo и имеет одну таблицу с именем testfire со следующими характеристиками столбца:

TEST_ID( INT(11), PRIMARY, AUTO INCREMENT)
TEST_STRING( VARCHAR(50) )
TEST_INTEGER( INT(11) )
TEST_FLOAT( FLOAT )
TEST_DATE( DATETIME )

С 3 рядами:

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

Я пытался получить данные с помощью соединения Excel, в основном, чтобы проверить, работает ли драйвер. Excel успешно извлек данные без проблем, поэтому DNS с именем TestConnection действителен, как и учетные данные.

  • Что я делаю не так?
  • Что я должен изменить?
  • Преобразование в MYSQLWCHAR * портит строку подключения?
  • Есть ли другой, возможно, лучший и более эффективный подход? (кроме, возможно, инкапсуляции классов, это то, что я собираюсь сделать после успешного прохождения теста)

О, и компилятор не выдает никаких ошибок или предупреждений, код скомпилирован и работает без проблем.

Итак, вот тестовый код, который возвращает «Ошибка выполнения запроса»:

#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

После обновления кода (и публикации) с использованием правильных аргументов для SQLDriverConnect из документации, предоставленной Mat (см. Комментарии ниже), соединение работает. Как я могу сделать то же самое, не запрашивая DNS-имя? Поставить дескриптор окна как ноль и ...?

Теперь он выходит из строя на SQLExecDirect(hstmt, (SQLWCHAR*)"SELECT TEST_STRING, TEST_INTEGER, TEST_FLOAT FROM dbo.testfire", SQL_NTS), но запрос правильный, так в чем же проблема?

Точное возвращаемое сообщение об ошибке:

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.

Так ... что это значит? Как я могу не иметь разрешения? Как это может генерировать синтаксическую ошибку, это явно правильный запрос?

Ответы [ 2 ]

6 голосов
/ 22 октября 2011

С небольшой помощью Мэта я смог выяснить, в чем проблема, но, поскольку он не дал ее в форме ответа, мне придется ответить на нее, чтобы ее могли поделитьсяу кого такая же проблема, а также пометить как ответивший.

Итак, моя проблема заключалась в том, что я не смог подключиться к базе данных.Как предложил Мат, я должен использовать расширенную информацию об ошибках, известную как SQLGetDiagRec, а также исправить аргументы в соответствии с документацией.Мне потребовалось время, чтобы узнать, как работает функция SQLGetDiagRec, но как только мне удалось преобразовать wchar_t в char *, я смог увидеть ошибку, которую она генерировала.

Попытка подключения дала мнеошибка Data source not found and no default driver specified.Это дало мне подсказку, указав, что я либо написал неверную строку подключения, либо что текстовая строка была каким-то образом неверно истолкована или искажена.

Выполнение поиска в сети дало мне понимание того, что строкабыл неверно истолкован, и чтобы исправить это, я должен был сделать его буквальной строкой.Конечно же, поставив L перед строкой, это решило!

retcode = SQLDriverConnect(hdbc, 0, 
                           (SQLWCHAR*)L"DSN=TestConnection;SERVER=localhost;UID=user;PWD=password;DRIVER=MySQL Server;", 
                           _countof(L"DSN=TestConnection;SERVER=localhost;UID=user;PWD=password;DRIVER=MySQL Server;"), 
                           OutConnStr, 255, &OutConnStrLen, SQL_DRIVER_COMPLETE);

В то же время я узнал, как избавиться от приглашения, которое было довольно легко выяснить после исправления исходной проблемы.,Укажите null для дескриптора окна, установите для завершения драйвера значение SQL_DRIVER_COMPLETE и убедитесь, что вы добавили всю информацию, необходимую в строке подключения.

Итак, следующая проблема, с которой я столкнулся при выполнении запроса с SQLExecDirect,ошибка, говорящая Syntax error or access violation.Проблема была, очевидно, такой же, как со строкой подключения.Конечно же,

retcode = SQLExecDirect(hstmt, (SQLWCHAR*)L"SELECT TEST_STRING, TEST_INTEGER, TEST_FLOAT FROM dbo.testfire", SQL_NTS);

Работает как шарм.

Вот код в полном объеме, полностью функциональный:

#include <iostream>
#include <windows.h>
#include <sql.h>
#include <sqltypes.h>
#include <sqlext.h>
#include <string>

using namespace std;

int main(){
    SQLHENV henv;
    SQLHDBC hdbc;
    SQLHSTMT hstmt;
    SQLRETURN retcode;

    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, (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, 
                    0,
                    (SQLWCHAR*)L"DSN=TestConnection;SERVER=localhost;UID=root;PWD=never140;DRIVER=MySQL Server;", 
                    _countof(L"DSN=TestConnection;SERVER=localhost;UID=root;PWD=never140;DRIVER=MySQL Server;"),
                    OutConnStr,
                    255, 
                    &OutConnStrLen,
                    SQL_DRIVER_COMPLETE );

                // 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*)L"SELECT TEST_STRING, TEST_INTEGER, TEST_FLOAT FROM dbo.testfire", SQL_NTS);

                    if (retcode == SQL_SUCCESS) {
                        SQLINTEGER sTestInt, cbTestStr, cbTestInt, cbTestFloat, iCount = 1;
                        SQLFLOAT dTestFloat;
                        SQLCHAR szTestStr[200];
                        while (TRUE) {
                            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_DOUBLE, &dTestFloat, 0,&cbTestFloat);

                                /* Print the row of data */
                                cout<<"Row "<<iCount<<":"<<endl;
                                cout<<szTestStr<<endl;
                                cout<<sTestInt<<endl;
                                cout<<dTestFloat<<endl;
                                iCount++;
                            } else {
                                break;
                            }
                        }
                    }else{
                        cout<<"Query execution error."<<endl;
                    }

                    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;
}

Просто, чтобы показать, даже самая маленькая вещьможет все провалиться.

Спасибо, Мат, за помощь.

0 голосов
/ 28 декабря 2012

изменить (SQLWCHAR *) на L. это прекрасно работает для меня

...