使用SQLDescribeCol()/SQLBindCol()无法获取查询结果的技术咨询
从你贴的GetTableOwner方法片段来看,有几个明显的问题会导致你拿不到查询结果,咱们一步步捋:
1. 不完整的SQL语句
你写的查询语句是截断的:
query = L"SELECT cast(su.name AS nvarchar(128)) FRO..."
这根本不是一个合法的SQL语句,数据库执行时直接就会报错,后续的SQLDescribeCol和SQLBindCol自然没有结果可以处理。针对SQL Server,正确的查询表所有者的完整语句应该类似:
SELECT CAST(su.name AS NVARCHAR(128)) FROM sys.tables st JOIN sys.schemas ss ON st.schema_id = ss.schema_id JOIN sys.database_principals su ON st.principal_id = su.principal_id WHERE ss.name = ? AND st.name = ?
2. 缺失的ODBC句柄初始化
代码里声明了SQLHSTMT stmt = 0; SQLHDBC hdbc = 0;,但完全没看到你怎么获取有效的连接句柄hdbc和语句句柄stmt。你得先确保hdbc是已经成功连接的ODBC连接,然后调用SQLAllocHandle创建语句句柄:
SQLRETURN ret = SQLAllocHandle(SQL_HANDLE_STMT, pimpl->m_hdbc, &stmt); if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO) { // 这里要添加错误处理,把错误信息写入errorMsg return -1; }
如果stmt是空的,所有后续的ODBC函数调用都会直接失败。
3. 未处理的内存与绑定问题
你声明了SQLWCHAR *owner = NULL;,但没有给它分配内存。调用SQLBindCol时需要提供足够大的缓冲区来存储查询到的所有者名称,比如静态分配缓冲区:
SQLWCHAR ownerBuffer[129]; // 对应查询里的nvarchar(128),加1存结束符 SQLLEN ownerLen = 0; SQLRETURN ret = SQLBindCol(stmt, 1, SQL_C_WCHAR, ownerBuffer, sizeof(ownerBuffer)/sizeof(SQLWCHAR), &ownerLen);
如果想动态分配,也可以先调用SQLDescribeCol获取列的长度,再分配对应大小的内存。
4. 缺失的错误检查逻辑
你的代码里几乎没有对ODBC函数调用的返回值做检查。每一次调用ODBC API(比如SQLExecDirect、SQLDescribeCol、SQLBindCol、SQLFetch)之后,都应该检查返回码,一旦遇到SQL_ERROR或其他失败状态,要调用SQLGetDiagRec获取详细错误信息,写入errorMsg向量:
SQLRETURN ret = SQLExecDirect(stmt, (SQLWCHAR*)query.c_str(), SQL_NTS); if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO) { SQLWCHAR errMsg[256]; SQLWCHAR sqlState[6]; SQLINTEGER nativeErr; SQLSMALLINT msgLen; SQLGetDiagRec(SQL_HANDLE_STMT, stmt, 1, sqlState, &nativeErr, errMsg, sizeof(errMsg)/sizeof(SQLWCHAR), &msgLen); errorMsg.push_back(std::wstring(errMsg)); return -1; }
没有错误检查的话,你根本不知道是执行SQL出错,还是绑定列出错,排查起来毫无头绪。
5. 不安全的SQL语句拼接(可选优化)
你现在的代码看起来想直接把schemaName和tableName拼到SQL里,这不仅有SQL注入风险,还会因为特殊字符(比如含空格、引号的表名)导致SQL语法错误。建议用参数化查询:
// 先准备带参数的SQL std::wstring query = L"SELECT CAST(su.name AS NVARCHAR(128)) FROM sys.tables st JOIN sys.schemas ss ON st.schema_id = ss.schema_id JOIN sys.database_principals su ON st.principal_id = su.principal_id WHERE ss.name = ? AND st.name = ?"; SQLRETURN ret = SQLPrepare(stmt, (SQLWCHAR*)query.c_str(), SQL_NTS); // 绑定schema参数 ret = SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_WCHAR, SQL_WVARCHAR, schemaName.size(), 0, (SQLPOINTER)schemaName.c_str(), 0, NULL); // 绑定table参数 ret = SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_WCHAR, SQL_WVARCHAR, tableName.size(), 0, (SQLPOINTER)tableName.c_str(), 0, NULL); // 执行查询 ret = SQLExecute(stmt);
这样既安全又能避免语法错误。
先把这些问题逐一修复,应该就能解决你无法获取查询结果的问题了。
内容的提问来源于stack exchange,提问作者Igor

