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
相关产品推荐
相关产品推荐

