c++ - 使用 C++ 连接到 MySQL 服务器

标签 c++ mysql odbc driver visual-c++-2008

我正在尝试通过 Visual C++ 2008 Express Edition 上的 MySQL ODBC 5.1 驱动程序使用 C++ 连接到 MySQL 服务器。

我正在按照 MSDN 中的这些说明进行操作:

唯一的区别是我必须将所有 SQLCHAR 转换为 SQLWCHAR,以匹配函数参数,希望这不会影响连接字符串。

每次连接时,我都会得到 SQL_ERROR 作为返回值。 所以我假设连接字符串或连接语句有问题。

我试过了

DNS=测试连接; UID=用户; PSW=密码

服务器=本地主机;驱动程序={MySQL ODBC 5.1 驱动程序};端口=3306; UID=用户; PSW=密码;数据库=dbo; 和其他类似的连接字符串。

名为 TestConnection 的 DNS 与后一个连接字符串具有相同的信息。

架构是 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 成功地检索了数据,没有问题,因此名为 TestConnection 的 DNS 有效,凭据也是有效的。

  • 我做错了什么?
  • 我应该改变什么?
  • 是不是转换为 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;
}

更新

使用 Mat 提供的文档中的 SQLDriverConnect 的正确参数更新代码(和发布)后(请参阅下面的评论),连接正常。如何在不提示输入 DNS 名称的情况下做同样的事情?将窗口句柄设为 null 并...?

现在它在 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.

所以……那是什么意思? 没有权限怎么办? 这怎么会产生语法错误,它显然是一个有效的查询?

最佳答案

在 Mat 的帮助下,我能够找出问题所在,但由于他没有以答案的形式给出,我必须回答它以便与那些人共享谁有同样的问题,也标记为已回答。

所以,我的问题是无法连接到数据库。正如 Mat 所建议的,我应该使用扩展的错误信息,称为 SQLGetDiagRec 并根据文档修复参数。我花了一些时间了解 SQLGetDiagRec 函数是如何工作的,但是一旦我设法将 wchar_t 转换为 char * 我就能够看到它产生的错误。

连接尝试给了我错误Data source not found and no default driver specified。这给了我一个线索,表明我写了不正确的连接字符串,或者文本字符串以某种方式被误解或损坏。

做一些事情 searching on the net让我意识到字符串被误解了,为了修复它,我必须把它变成一个文字字符串。果然,在字符串前面放一个 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;
}

只是去展示,即使是最微小的事情也会让一切失败。

谢谢 Mat 的帮助。

关于c++ - 使用 C++ 连接到 MySQL 服务器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7860480/

相关文章:

c++ - boost 正则表达式定义

c++ - 如何将值从一个指针复制到另一个

c++ - 如何让 Q_PROPERTY 在设计 View 中显示

php - 如何从mysql查询最大id?

mysql - 将有效的 mysql 查询转换为 Mathematica 语句

mysql - 如何将 3 个表中的组合插入到具有 3 个相应列的第 4 个表中?

sqlite - sqlite3表无法链接到Access

c++ - Arduino倒数计时器阻止程序运行的其余部分

sql-server - 无法使用SQL Server ODBC驱动程序执行同义词存储过程;与OLEDB一起使用

pdo - Zend PDO ODBC 数据库选择