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

MySQL预处理语句查INNODB_TABLES返回SQL_NO_DATA但记录存在的问题

问题描述

我尝试执行以下两条MySQL查询语句:

std::wstring query1 = L"SELECT st.table_id FROM information_schema.INNODB_TABLES st  WHERE st.name = ?;";

或

std::wstring query1 = L"SELECT st.table_id FROM information_schema.INNODB_TABLES st  WHERE st.name = CONCAT(?, '/', ?);";

使用预处理语句执行时返回SQL_NO_DATA,但直接将参数拼写进语句中却能查询到对应记录。以下是完整的C++代码:

auto res1 = mysql_stmt_init( m_db );
if( !res1 )
{
    std::wstring err = m_pimpl->m_myconv.from_bytes( mysql_stmt_error( res1 ) );
    errors.push_back( err );
    result = 1;
}
else
{
    if( mysql_stmt_prepare( res1, m_pimpl->m_myconv.to_bytes( query1.c_str() ).c_str(), query1.length() ) )
    {
        std::wstring err = m_pimpl->m_myconv.from_bytes( mysql_stmt_error( res1 ) );
        errors.push_back( err );
        result = 1;
    }
    else
    {
        MYSQL_BIND params[2];
        unsigned long str_length1, str_length2;
        str_length1 = strlen( m_pimpl->m_myconv.to_bytes( schema.c_str() ) .c_str() ) * 2;
        str_length2 = strlen( m_pimpl->m_myconv.to_bytes( table.c_str() ).c_str() ) * 2;
        char *str_data1 = new char[str_length1], *str_data2 = new char[str_length2];
        memset( str_data1, '\0', str_length1 );
        memset( str_data2, '\0', str_length2 );
        memset( params, 0, sizeof( params ) );
        strncpy( str_data1, m_pimpl->m_myconv.to_bytes( schema.c_str() ) .c_str(), str_length1 );
        strncpy( str_data2, m_pimpl->m_myconv.to_bytes( table.c_str() ).c_str(), str_length2 );
        params[0].buffer_type = MYSQL_TYPE_STRING;
        params[0].buffer = (char *) str_data1;
        params[0].buffer_length = strlen( str_data1 );
        params[0].is_null = 0;
        params[0].length = &str_length1;
        params[1].buffer_type = MYSQL_TYPE_STRING;
        params[1].buffer = (char *) str_data2;
        params[1].buffer_length = strlen( str_data2 );
        params[1].is_null = 0;
        params[1].length = &str_length2;
        if( mysql_stmt_bind_param( res1, params ) )
        {
            std::wstring err = m_pimpl->m_myconv.from_bytes( mysql_stmt_error( res1 ) );
            errors.push_back( err );
            result = 1;
        }
        else
        {
            auto prepare_meta_result = mysql_stmt_result_metadata( res1 );
            if( !prepare_meta_result )
            {
                std::wstring err = m_pimpl->m_myconv.from_bytes( mysql_stmt_error( res1 ) );
                errors.push_back( err );
                result = 1;
            }
            else
            {
                if( mysql_stmt_execute( res1 ) )
                {
                    std::wstring err = m_pimpl->m_myconv.from_bytes( mysql_stmt_error( res1 ) );
                    errors.push_back( err );
                    result = 1;
                }
                else
                {
                    MYSQL_BIND results1[1];
                    bool is_null[1], error[1];
                    unsigned long length[1];
                    memset( results1, 0, sizeof( results1 ) );
                    results1[0].buffer_type = MYSQL_TYPE_LONG;
                    
                    results1[0].buffer = (char *) &tableId;
                    results1[0].is_null = &is_null[1];
                    results1[0].error = &error[1];
                    results1[0].length = &length[1];
                    
                    if( mysql_stmt_bind_result( res1, results1 ) )
                    {
                        std::wstring err = m_pimpl->m_myconv.from_bytes( mysql_stmt_error( res1 ) );
                        errors.push_back( err );
                        result = 1;
                    }
                    else
                    {
                        while( true )
                        {
                            auto dataset = mysql_stmt_fetch( res1 );
                            if( dataset == 1 || dataset == MYSQL_NO_DATA )
                                break;
                            else
                                id = tableId;
                        }
                        mysql_free_result( prepare_meta_result );
                    }
                }
            }
        }
    }
}
if( mysql_stmt_close( res1 ) )
{
    std::wstring err = m_pimpl->m_myconv.from_bytes( mysql_stmt_error( res1 ) );
    errors.push_back( err );
    result = 1;
}

请问我遗漏了什么导致该问题出现?

问题原因分析与修正方案

1. 字符串长度计算错误

  • 错误点:计算str_length1和str_length2时,将strlen结果乘以2是多余的。strlen返回的是字符串的实际字节数,MySQL预处理参数的length字段只需要这个原始值,不需要额外放大。
  • 错误代码:
    str_length1 = strlen( m_pimpl->m_myconv.to_bytes( schema.c_str() ) .c_str() ) * 2;
    str_length2 = strlen( m_pimpl->m_myconv.to_bytes( table.c_str() ).c_str() ) * 2;
    
  • 修正:直接使用字符串的字节长度,更安全的方式是借助std::string的size()方法:
    std::string schema_str = m_pimpl->m_myconv.to_bytes(schema.c_str());
    str_length1 = schema_str.size();
    std::string table_str = m_pimpl->m_myconv.to_bytes(table.c_str());
    str_length2 = table_str.size();
    

2. buffer_length参数设置错误

  • 错误点:buffer_length应该是你分配的缓冲区总大小,而不是strlen(str_data1)(这是字符串的有效长度)。错误设置会导致MySQL可能截断参数数据,导致查询不匹配。
  • 错误代码:
    params[0].buffer_length = strlen( str_data1 );
    params[1].buffer_length = strlen( str_data2 );
    
  • 修正:改为你分配的缓冲区实际大小:
    params[0].buffer_length = str_length1;
    params[1].buffer_length = str_length2;
    

3. 结果绑定数组越界访问

  • 错误点:is_null、error、length数组的大小为1,索引从0开始,但你使用了&is_null[1]这类索引1的取值,会触发数组越界,破坏内存结构,进而影响查询结果的处理逻辑。
  • 错误代码:
    results1[0].is_null = &is_null[1];
    results1[0].error = &error[1];
    results1[0].length = &length[1];
    
  • 修正:改为索引0的正确取值:
    results1[0].is_null = &is_null[0];
    results1[0].error = &error[0];
    results1[0].length = &length[0];
    

4. 字符集不匹配问题

  • 检查MySQL连接的字符集是否与information_schema.INNODB_TABLES中name字段的字符集一致(通常为UTF-8)。字符集不匹配会导致参数编码转换错误,使得查询条件无法匹配记录。
  • 解决方案:建立连接后执行SET NAMES utf8mb4;语句统一字符集。

5. 内存泄漏风险(额外优化)

  • 你用new分配了str_data1和str_data2,但没有释放,会导致内存泄漏。建议使用std::unique_ptr<char[]>自动管理内存,或者在使用完后手动执行delete[] str_data1; delete[] str_data2;。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 06:35:26