我正在使用Microsoft的ODBC驱动程序将C ++ / Linux应用程序连接到远程运行的SQL Server数据库,并且当我尝试连接到数据库时,调用失败,显示为SQL_INVALID_HANDLE
。通读他们的documentation,我发现:
SQL_INVALID_HANDLE函数由于无效的环境,连接,语句或描述符句柄而失败。这表示编程错误。 SQLGetDiagRec或SQLGetDiagField没有其他信息。仅当句柄是空指针或错误类型时(例如,为需要连接句柄的参数传递语句句柄时),才返回此代码。
足够公平,但是在connect语句之前的句柄和环境创建过程中,没有任何错误。另外,对于第二个参数,他们的文档说如果没有桌面窗口,我可以传递一个空指针(在此Linux控制台应用程序中就是这种情况)。这是根据微软的example program改编而成的MVCE:
#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
显然是正确的类型,并且在调试器中对其进行检查后发现它不是null。
可能值得注意的是,使用pyodbc
在Python程序中可以使用完全相同的连接字符串。不过,似乎C ++程序甚至还远远没有看到该字符串。只是不喜欢我发送给connect调用的句柄。
Microsoft的文档明确指出他们没有提供其他信息。如果有人可以提供任何有关如何诊断/调试的指导,我将不胜感激。
此应用程序在Centos 7上使用gcc 4.9.1。
答案 0 :(得分:0)
在Unix中,句柄不必为显示对话框而必须为非null值。
答案 1 :(得分:0)
经过两周的挖掘,结果发现这是某种版本控制问题。
最终,该程序将通过Microsoft在libmsodbcsql.so
中的扩展名进行一些BCP上传。事实证明,该库还具有许多SQL*
函数的实现,这些函数在该测试程序中失败。当我更改链接的顺序,使libodbc.so
在MSFT扩展库之前,以便加载程序首先找到那些实现时,程序运行正常。
我很好奇为什么会这样,这可能表明我做错了其他事情,这可能会困扰我。但是至少到目前为止,我已经能够连接到数据库并进行基本的查询和更新。
感谢那些帮助过的人。
答案 2 :(得分:0)
对于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;
}
答案 3 :(得分:0)
使用相似的代码,我有完全相同的错误(在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)