You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Linux下ODBC C++程序SQLConnect时因get_charset_by_csname崩溃排查

Linux环境下ODBC连接TD-SQL时SQLConnect崩溃排查分析

问题描述

  • Windows环境中程序可正常通过ODBC连接TD-SQL,但Linux环境下执行SQLConnect时崩溃,崩溃栈见相关截图
  • MY_SQL_CHAR在Linux中定义为SQLCHAR,在Windows中定义为SQLWCHAR
  • 同类代码单独运行无异常,集成到大项目后出现相同崩溃

核心代码

int COdbcConnect::Connect(const char* pDataSource)
{
    LOG_WARNING(g_pLogHandle, "xxxxxxxxxx, pDataSource:%s", pDataSource); ///test3
    m_strDataSource = std::string(pDataSource);
    SQLRETURN ret;
    if (m_pEnv == NULL)
    {
        if (false == initEnv(m_pEnv)) ///test3
        {
            return 1;
        }
    }
    UTIL_Strcpy(m_pEnv->dataSource, MAX_DATA_SOURCE_LENGTH, pDataSource);
 
    CloseConnect();
    try
    {
        ret = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &(m_pEnv->hEnv));

        if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO)
        {
            m_bConnected = false;
            ONLY_CHECK_ERROR(ret,"Connect: when exec SQLAllocHandle",m_pEnv->hEnv,SQL_HANDLE_ENV)
            SQLFreeHandle(SQL_HANDLE_ENV, m_pEnv->hEnv);
            return 2;
        }

        ret=SQLSetEnvAttr((m_pEnv->hEnv), SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3, 0);
        if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO)
        {
            m_bConnected = false;
            ONLY_CHECK_ERROR(ret,"Connect: when exec SQLAllocHandle",m_pEnv->hEnv,SQL_HANDLE_ENV)
                SQLFreeHandle(SQL_HANDLE_ENV, m_pEnv->hEnv);
            return 3;
        }

        ret = SQLAllocHandle(SQL_HANDLE_DBC, m_pEnv->hEnv, &(m_pEnv->hDbc));
        if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO) {
            m_bConnected = false;
            ONLY_CHECK_ERROR(ret,"Connect: allocate odbc connection handler",m_pEnv->hEnv,SQL_HANDLE_ENV);
            SQLFreeHandle(SQL_HANDLE_DBC, m_pEnv->hDbc);
            SQLFreeHandle(SQL_HANDLE_ENV, m_pEnv->hEnv);
            return 4;
        }

        LOG_WARNING(g_pLogHandle, "ret = %d, datasource = %p, %s, m_pEnv->hDbc:%p", ret, m_pEnv->dataSource, m_pEnv->dataSource, m_pEnv->hDbc); ///test3
        ret=SQLConnect((m_pEnv->hDbc), (MY_SQL_CHAR*)"smarteazy",
            SQL_NTS, (MY_SQL_CHAR *)NULL, SQL_NTS, (MY_SQL_CHAR *)NULL,
            SQL_NTS); ///test3
       //const char* pAbc = "smarteazy";
       //ret=SQLConnect((m_pEnv->hDbc), (MY_SQL_CHAR*)pAbc,
       //     SQL_NTS, (MY_SQL_CHAR *)NULL, SQL_NTS, (MY_SQL_CHAR *)NULL,
       //     SQL_NTS); ///test3
       //ret=SQLConnect((env->hDbc), (SQLCHAR*)env->data_source,
       //    SQL_NTS, (SQLCHAR *)NULL, SQL_NTS, (SQLCHAR *)NULL,
       //    SQL_NTS);


        //LOG_WARNING(g_pLogHandle, "ret = %d, datasource = %s", ret, m_pEnv->dataSource); ///test3
        if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO) {
            // 连接失败
            m_bConnected = false;
            ONLY_CHECK_ERROR(ret,"Connect: when exec SQLConnect",m_pEnv->hEnv,SQL_HANDLE_ENV);
            SQLFreeHandle(SQL_HANDLE_DBC, m_pEnv->hDbc);
            SQLFreeHandle(SQL_HANDLE_ENV, m_pEnv->hEnv);
            return 5;
        }


        ret = SQLAllocHandle(SQL_HANDLE_STMT, (m_pEnv->hDbc), &(m_pEnv->hStmt));
        if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO)
        {
            m_bConnected = false;
            ONLY_CHECK_ERROR(ret,"Connect: when exec SQLAllocHandle",m_pEnv->hEnv,SQL_HANDLE_ENV)
            SQLFreeHandle(SQL_HANDLE_STMT, m_pEnv->hStmt);
            SQLFreeHandle(SQL_HANDLE_DBC, m_pEnv->hDbc);
            SQLFreeHandle(SQL_HANDLE_ENV, m_pEnv->hEnv);
            return 6;
        }
        else
        {
            m_bConnected = true;
        }
    }
    catch (char* pError)
    {
        LOG_WARNING(g_pLogHandle, "catch exception error:%s", pError);
        m_bConnected = false;
        ONLY_CHECK_ERROR(ret,"Connect: when exec SQLAllocHandle",m_pEnv->hEnv,SQL_HANDLE_ENV);
        SQLFreeHandle(SQL_HANDLE_STMT, m_pEnv->hStmt);
        SQLFreeHandle(SQL_HANDLE_DBC, m_pEnv->hDbc);
        SQLFreeHandle(SQL_HANDLE_ENV, m_pEnv->hEnv);
        return 7;
    }

    return 0;
}

排查方向

1. 字符编码与类型转换问题

Linux下MY_SQL_CHAR是单字节SQLCHAR,代码中直接将字符串常量"smarteazy"强制转换为MY_SQL_CHAR*,看似兼容,但需注意:

  • TD-SQL的Linux ODBC驱动可能要求特定字符集(如UTF-8),大项目中若全局设置了其他编码(如GBK),会导致字符串编码混乱,触发崩溃
  • 建议避免直接强制转换,根据MY_SQL_CHAR类型构造对应字符串:Linux下用SQLCHAR数组存储,Windows下用宽字符串前缀L,或用ODBC标准转换函数统一处理字符集

2. 环境变量与驱动依赖冲突

大项目可能修改了ODBC相关环境变量(如ODBCINI、ODBCSYSINI),导致加载错误的驱动版本,或驱动依赖库与项目其他库冲突:

  • 打印大项目启动前后的ODBC环境变量,对比单独运行时的变量值
  • 用ldd命令检查项目可执行文件依赖的ODBC驱动库版本,确认与单独运行时一致

3. 内存管理与缓冲区溢出

  • 检查CloseConnect()实现,确保每次连接前完全释放hEnv、hDbc等ODBC句柄,避免野指针或重复分配
  • 验证UTIL_Strcpy的安全性:若pDataSource长度超过MAX_DATA_SOURCE_LENGTH,会导致缓冲区溢出,破坏m_pEnv的成员结构,大项目内存布局更紧凑,更容易触发崩溃

4. 线程安全问题

大项目多线程环境下,ODBC句柄(尤其是环境句柄hEnv)若未做线程隔离,多线程同时操作会导致崩溃:

  • 确认m_pEnv是否为线程私有数据,或在多线程调用Connect时添加互斥锁保护

5. 编译选项差异

大项目与测试程序的编译选项(优化等级、对齐规则、宏定义)不同,可能导致m_pEnv结构体内存布局变化,引发访问偏移错误:

  • 对比两者的编译参数,确保ODBC相关结构体的编译选项一致(如对齐方式、是否开启内存优化)

内容的提问来源于stack exchange,提问作者eric

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 14:04:55