在 C 中使用 ODBC 的问题 - sqlState 返回 IM008(连接字符串可能存在问题?)

问题描述 投票:0回答:1

我在 C 中的以下实现遇到问题。(当然我已经模糊了连接字符串中的敏感信息)。

#include <windows.h>
#include <sqlext.h>
#include <sqltypes.h>
#include <sql.h>
#include <stdio.h>

int main() {
    SQLHENV hEnv;
    SQLHDBC hDbc;
    SQLRETURN ret; /* ODBC API return status */

    SQLCHAR* connStr = (SQLCHAR*)"DRIVER={ODBC Driver 17 for SQL Server}; SERVER={tcp:xxx.xxx.xxx.xxx,xxxx}; UID=****; PWD=*****; DATABASE={SDC};\0";

    printf("%s\n", connStr);


    // Allocate an environment handle
    ret = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &hEnv);
    if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO) {
        printf("Error allocating environment handle\n");
        return -1;
    }

    // Set the ODBC version environment attribute
    ret = SQLSetEnvAttr(hEnv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3, 0);
    if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO) {
        printf("Error setting environment attribute\n");
        SQLFreeHandle(SQL_HANDLE_ENV, hEnv);
        return -1;
    }

    // Allocate a connection handle
    ret = SQLAllocHandle(SQL_HANDLE_DBC, hEnv, &hDbc);
    if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO) {
        printf("Error allocating connection handle\n");
        SQLFreeHandle(SQL_HANDLE_ENV, hEnv);
        return -1;
    }

    // Set login timeout to 5 seconds
    SQLSetConnectAttr(hDbc, SQL_LOGIN_TIMEOUT, (SQLPOINTER)5, 0);

    // Connect to the SQL Server
    ret = SQLDriverConnect(hDbc, NULL, connStr, SQL_NTS, NULL, 0, NULL, SQL_DRIVER_COMPLETE);

    if (ret == SQL_ERROR) {
        SQLCHAR sqlState[12] = { 0 };  // 5 characters + null terminator
        SQLINTEGER nativeError;
        SQLCHAR messageText[2048] = { 0 };  // Message buffer
        SQLSMALLINT messageLength;

        // Fetch diagnostic information
        ret = SQLGetDiagRec(SQL_HANDLE_DBC, hDbc, 1, sqlState, &nativeError, messageText, sizeof(messageText), &messageLength);

        // Null-terminate the message if it exceeds the buffer size
        if (messageLength > sizeof(messageText) - 1) {
            messageText[sizeof(messageText) - 1] = '\0';
        }

        printf("SQLSTATE: %s\n", sqlState);
        printf("Native Error Code: %d\n", nativeError);
        printf("Message: %s\n", messageText);
    }
    else if (ret == SQL_SUCCESS_WITH_INFO) {
        printf("Connection successful with info!\n");
    }
    else if (ret == SQL_SUCCESS) {
        printf("Connection successful!\n");
    }
    else {
        printf("Error connecting to the database\n");
        SQLFreeHandle(SQL_HANDLE_DBC, hDbc);
        SQLFreeHandle(SQL_HANDLE_ENV, hEnv);
        return -1;
    }

    // Close the connection and free handles
    SQLDisconnect(hDbc);
    SQLFreeHandle(SQL_HANDLE_DBC, hDbc);
    SQLFreeHandle(SQL_HANDLE_ENV, hEnv); 

    return 0;
}

有效的 Python 实现:

import pyodbc as sql
import pandas as pd
import sys
import warnings
from time import (strftime, strptime)


server = 'tcp:xxx.xxx.xxx.xxx,xxxx'
db = 'SDC'

string = "DRIVER={ODBC Driver 17 for SQL Server}; SERVER={"+server+"}; UID=****; PWD=*****; DATABASE={"+db+"}; "

print(string)

try:
    db = sql.connect(string)
    print('It worked!')
except Exception as ex:
    print(ex)
    


它返回对话框失败消息文本和 sqlState IM008。从我在网上看到的情况来看,可能是登录凭据错误。然而,该字符串的格式与工作 Python 实现中的格式完全相同(位于 C 代码下方)。有谁知道我做错了什么? printf 语句完全按照我想要的方式显示字符串。

解决方案:

#include <windows.h>
#include <sqlext.h>
#include <sqltypes.h>
#include <sql.h>
#include <stdio.h>
#include <wchar.h>

int main() {
    SQLHENV hEnv;
    SQLHDBC hDbc;
    SQLRETURN ret; /* ODBC API return status */

    wchar_t connStr[] = L"DRIVER={ODBC Driver 17 for SQL Server}; SERVER={tcp:xxx.xxx.xxx.xxx,xxxx}; UID=****; PWD=*****; DATABASE={SDC};";

    // Allocate an environment handle
    ret = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &hEnv);
    if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO) {
        wprintf(L"Error allocating environment handle\n");
        return -1;
    }

    // Set the ODBC version environment attribute
    ret = SQLSetEnvAttr(hEnv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3, 0);
    if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO) {
        wprintf(L"Error setting environment attribute\n");
        SQLFreeHandle(SQL_HANDLE_ENV, hEnv);
        return -1;
    }

    // Allocate a connection handle
    ret = SQLAllocHandle(SQL_HANDLE_DBC, hEnv, &hDbc);
    if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO) {
        wprintf(L"Error allocating connection handle\n");
        SQLFreeHandle(SQL_HANDLE_ENV, hEnv);
        return -1;
    }

    // Set login timeout to 5 seconds
    SQLSetConnectAttr(hDbc, SQL_LOGIN_TIMEOUT, (SQLPOINTER)5, 0);

    // Connect to the SQL Server
    ret = SQLDriverConnectW(hDbc, NULL, connStr, SQL_NTS, NULL, 0, NULL, SQL_DRIVER_COMPLETE);

    if (ret == SQL_ERROR || ret == SQL_SUCCESS_WITH_INFO) {
        SQLWCHAR sqlState[6] = { 0 };  // 5 characters + null terminator
        SQLINTEGER nativeError;
        SQLWCHAR messageText[512] = { 0 };  // Message buffer
        SQLSMALLINT messageLength;
        int i = 1;

        // Fetch diagnostic information
        while (SQLGetDiagRecW(SQL_HANDLE_DBC, hDbc, i, sqlState, &nativeError, messageText, sizeof(messageText) / sizeof(SQLWCHAR), &messageLength) != SQL_NO_DATA) {
            // Null-terminate the message if it exceeds the buffer size
            if (messageLength > sizeof(messageText) / sizeof(SQLWCHAR) - 1) {
                messageText[sizeof(messageText) / sizeof(SQLWCHAR) - 1] = L'\0';
            }

            wprintf(L"SQLSTATE: %ls\n", sqlState);
            wprintf(L"Native Error Code: %d\n", nativeError);
            wprintf(L"Message: %ls\n", messageText);
            i++;
        }
    }

    if (ret == SQL_SUCCESS_WITH_INFO) {
        wprintf(L"Connection successful with info!\n");
    }
    else if (ret == SQL_SUCCESS) {
        wprintf(L"Connection successful!\n");
    }
    else {
        wprintf(L"Error connecting to the database\n");
        SQLFreeHandle(SQL_HANDLE_DBC, hDbc);
        SQLFreeHandle(SQL_HANDLE_ENV, hEnv);
        return -1;
    }

    // Close the connection and free handles
    SQLDisconnect(hDbc);
    SQLFreeHandle(SQL_HANDLE_DBC, hDbc);
    SQLFreeHandle(SQL_HANDLE_ENV, hEnv);

    return 0;
}
python c sql-server odbc
1个回答
0
投票

正如 @pmg 的评论中正确指出的那样,问题是我没有在代码中使用 wchar(Windows 宽字符串)。更正的代码位于“解决方案”下编辑后的答案中。

© www.soinside.com 2019 - 2024. All rights reserved.