【问题标题】:Microsoft ODBC cannot create valid handleMicrosoft ODBC 无法创建有效句柄
【发布时间】:2018-09-25 21:11:09
【问题描述】:

我正在使用 Microsoft 的 ODBC 驱动程序将 C++/Linux 应用程序连接到远程运行的 SQL Server 数据库,当我尝试连接到数据库时,调用失败并显示 SQL_INVALID_HANDLE。阅读他们的documentation,我发现:

SQL_INVALID_HANDLE 由于环境、连接、语句或描述符句柄无效,函数失败。这表示编程错误。 SQLGetDiagRec 或 SQLGetDiagField 没有其他信息可用。仅当句柄为空指针或类型错误时才返回此代码,例如为需要连接句柄的参数传递语句句柄时。

很公平,但是在连接语句之前创建句柄和环境时,我没有任何错误。此外,对于第二个参数,他们的文档说如果没有桌面窗口(就像这个 linux 控制台应用程序一样),我可以传入一个空指针。这是一个MVCE,改编自微软的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;
}

程序输出SQLDriverConnect failed -2,即SQL_INVALID_HANDLE。我难住了。 hdbc 显然是正确的类型,在调试器中检查它显示它不为空。

值得注意的是,完全相同的连接字符串在使用pyodbc 的python 程序中工作。不过,似乎 C++ 程序甚至没有看到那个字符串。它只是不喜欢我发送到连接调用的句柄。

Microsoft 的文档清楚地表明它们不提供其他信息。如果有人可以提供有关如何诊断/调试此问题的任何指导,我将不胜感激。

此应用在 Centos 7 上使用 gcc 4.9.1。

【问题讨论】:

  • 我会在没有这些属性函数的情况下尝试它——它们看起来不正确,而且可能没有必要。
  • @NeilButterworth 删除它们的结果相同。
  • if ((!retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)) - 括号错误。
  • 好收获。但我修复了它并没有改变结果。
  • 请用更正的代码编辑您的问题

标签: c++ sql-server odbc


【解决方案1】:

经过两周的挖掘,结果证明这是某种版本控制问题。

最终,该程序将通过libmsodbcsql.so 中的 Microsoft 扩展进行一些 BCP 上传。事实证明,该库还实现了许多 SQL* 函数,这些函数在这个测试程序中失败了。当我更改链接的顺序以便 libodbc.so 在 MSFT 扩展库之前以便加载程序首先找到这些实现时,程序运行正常。

我很好奇为什么会这样,它可能指向我做错的其他事情,可能会在路上咬我。但至少现在,我能够连接到数据库并进行基本的查询和更新。

感谢帮助的人。

【讨论】:

  • 我遇到了完全相同的问题!我只花了 1 天的时间调试它,而不是多亏了这篇文章;这是一个虽然 cookie;看我的回答谢谢,StackOverflow
【解决方案2】:

在 Unix 中,句柄是否必须是非空值才能显示对话框。

【讨论】:

  • 对不起,我不明白你在说什么。对话框不是此应用程序/环境的一部分。
  • @John 对不起,没用。我的意思是在使用 SQL_DRIVER_PROMPT 时 dhandle 应该是 1 或其他东西而不是 0。我看到您已经尝试过 SQL_DRIVER_NOPROMPT 所以它不可能是那样。我的另一个想法是关于 inconnectionstring,而不是给出长度 dsn.length() 使用表示空终止字符串的 SQL_NTS。如果不是这样,我会尝试将字符串的长度加 1 作为参数。我注意到你使用 python 使用 pyobdc 可以与 C 一起使用吗?是否需要另一个 ODBC-ODBC 桥才能在 unix 中工作?
【解决方案3】:

对于 SQL Server 中的任何句柄,必须在使用前分配!

所以顺序是环境、连接和语句。

例子:

SQLHENV hEnv = nullptr;
SQLHDBC hDbc = nullptr;
SQLHSTMT hStmt = NULL;

分配

SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &hEnv);
SQLAllocHandle(SQL_HANDLE_DBC, hEnv, &hDbc);
SQLAllocHandle(SQL_HANDLE_STMT, hDbc, &hStmt);

以下是可能对您有所帮助的示例代码。

基本快速,

在你的 sql server 数据库中创建表并插入一些数据

create table test (id int, name nvarchar(128));

插入一些数据

insert into test (id,name) values (1, 'Awesome Name');

查询表中项目的C++代码

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

【讨论】:

    【解决方案4】:

    我在使用类似代码时遇到了完全相同的错误(在 Ubuntu 18.04 中工作,但更新到 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
    

    使用这个连接字符串

    DRIVER=ODBC Driver 17 for SQL Server;SERVER=127.0.0.1, 1433;UID=SA;PWD=password;DATABASE=my_database;
    

    这是我的图书馆链接顺序

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

    如上述解决方案所述,更改链接顺序解决了问题

    set(lib_dep ${lib_dep} pthread odbc dl msodbcsql-17)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-08-26
      • 1970-01-01
      • 2019-02-01
      • 1970-01-01
      • 2019-08-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多