MySQL的prepare使用及遇到bug解析過程
一、問題發(fā)現(xiàn)
在一次開發(fā)中使用 MySQL PREPARE 以后,從 prepare 直接取 name 賦值給 lex->prepared_stmt_name 然后給 EXECUTE 用,發(fā)現(xiàn)有一定概率找不到 prepare stmt 的 name,于是開始動(dòng)手調(diào)查問題發(fā)生的原因。
SQL語(yǔ)句示例:
CREATE TABLE t1 (a INT, b VARCHAR(10)); PREPARE dbms_sql_stmt4 FROM 'INSERT INTO t1 VALUES (1,''11'')'; EXECUTE dbms_sql_stmt4;
報(bào)錯(cuò):
SQL Error [1243] [HY000]: Unknown prepared statement handler (dbms_sql_stmt4??p??]UU) given to EXECUTE
二、問題調(diào)查過程
1、根據(jù)報(bào)錯(cuò)信息找到對(duì)應(yīng)源碼,發(fā)現(xiàn)在MySQL_sql_stmt_execute里面有判斷當(dāng)找不到 stmt name 時(shí)候報(bào)錯(cuò)信息。
這里的 name 此時(shí)已經(jīng)是亂碼了。
void MySQL_sql_stmt_execute(THD *thd) {
LEX *lex = thd->lex;
const LEX_CSTRING &name = lex->prepared_stmt_name;
DBUG_TRACE;
DBUG_PRINT("info", ("EXECUTE: %.*s\n", (int)name.length, name.str));
Prepared_statement *stmt;
if (!(stmt = thd->stmt_map.find_by_name(name))) {
my_error(ER_UNKNOWN_STMT_HANDLER, MYF(0), static_cast<int>(name.length),
name.str, "EXECUTE");
return;
}2、這個(gè) lex->prepared_stmt_name 是從 prepare name 中賦值的,于是調(diào)查 prepare 這個(gè) name 設(shè)置的函數(shù)。
bool Prepared_statement::set_name(const LEX_CSTRING &name_arg) {
m_name.length = name_arg.length;
m_name.str = static_cast<char *>(
memdup_root(m_arena.mem_root, name_arg.str, name_arg.length));
return m_name.str == nullptr;
}gdb 跟蹤代碼:
Thread 46 "MySQLd" hit Breakpoint 1, Prepared_statement::set_name (this=0x7fff2cbf3250, name_arg=...)
at /home/wuyy/greatdb/gitmerge/percona-server/sql/sql_prepare.cc:2447
2447 bool Prepared_statement::set_name(const LEX_CSTRING &name_arg) {
(gdb) n
2448 m_name.length = name_arg.length;
(gdb)
2450 memdup_root(m_arena.mem_root, name_arg.str, name_arg.length));
(gdb)
2449 m_name.str = static_cast<char *>(
(gdb)
2451 return m_name.str == nullptr;
(gdb) p m_name
$9 = {
str = 0x7fff2cd09a68 "dbms_sql_stmt4", '\217' <repeats 98 times>, "FLOAT",
length = 14
# 可以看到 m_name 后面出現(xiàn)了亂碼,說(shuō)明 m_nam e最后不是 \0 結(jié)束,而是別的字符。3、接著到 execute 的函數(shù)看一下這個(gè) name 值,發(fā)現(xiàn)確實(shí)后面跟的不是 \0 結(jié)束符,而是變?yōu)閬y碼。于是這里當(dāng)然會(huì)報(bào)錯(cuò)找不到該 stmt name 了。
Thread 46 "MySQLd" hit Breakpoint 2, MySQL_sql_stmt_execute (thd=0x7fff2c002688)
at /home/wuyy/greatdb/gitmerge/percona-server/sql/sql_prepare.cc:1944
1944 void MySQL_sql_stmt_execute(THD *thd) {
(gdb) n
1945 LEX *lex = thd->lex;
(gdb)
1946 const LEX_CSTRING &name = lex->prepared_stmt_name;
(gdb)
1947 DBUG_TRACE;
(gdb) p name
$10 = (const LEX_CSTRING &) @0x7fff2cd501e0: {
str = 0x7fff2cd09a68 "dbms_sql_stmt4\217\217p\271\221]UU",
length = 22
}
(gdb) n
1948 DBUG_PRINT("info", ("EXECUTE: %.*s\n", (int)name.length, name.str));
(gdb)
1951 if (!(stmt = thd->stmt_map.find_by_name(name))) {
(gdb)
1953 name.str, "EXECUTE");
(gdb)
1952 my_error(ER_UNKNOWN_STMT_HANDLER, MYF(0), static_cast<int>(name.length),
(gdb)
1954 return;
# 結(jié)果報(bào)錯(cuò)了。三、問題解決方案
通過以上 gdb 跟蹤過程我們可以發(fā)現(xiàn) prepare 存 name 的時(shí)候存放方式有問題導(dǎo)致 name 最后沒有結(jié)束符,于是回頭看一下set_name 的代碼,于是發(fā)現(xiàn)以下代碼問題:
bool Prepared_statement::set_name(const LEX_CSTRING &name_arg) {
m_name.length = name_arg.length;
m_name.str = static_cast<char *>(
memdup_root(m_arena.mem_root, name_arg.str, name_arg.length));←這里問題
return m_name.str == nullptr;
}
# 箭頭處發(fā)現(xiàn)存 name 時(shí)候申請(qǐng)的內(nèi)存長(zhǎng)度為 name_arg.length,沒有把最后的 \0 一起存放進(jìn)去,導(dǎo)致最后少了結(jié)束符,這就有概率導(dǎo)致查找 name 出錯(cuò)。于是把 name_arg.length 改為 name_arg.length+1,重新編譯代碼問題解決。
四、問題總結(jié)
c++ 中字符串的使用一定要注意最后的結(jié)束符\0,如果因?yàn)樯俜峙淞艘粋€(gè)長(zhǎng)度導(dǎo)致結(jié)束符沒有存進(jìn)去,最后存放的字符串就會(huì)產(chǎn)生問題。
到此這篇關(guān)于MySQL的prepare使用及遇到bug解析過程的文章就介紹到這了,更多相關(guān)mysql prepare使用內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
MySQL使用觸發(fā)器如何實(shí)現(xiàn)CHECK檢查約束的功能
這篇文章主要介紹了MySQL使用觸發(fā)器如何實(shí)現(xiàn)CHECK檢查約束的功能,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-01-01
允許任意IP訪問mysql數(shù)據(jù)庫(kù)的方法詳解
MYSQL默認(rèn)只能本地連接,即127.0.0.1和localhost,其他主機(jī)IP無(wú)法訪問數(shù)據(jù)庫(kù),那么如何允許任意IP訪問mysql數(shù)據(jù)庫(kù),所以本文小編將給大家介紹允許任意IP訪問mysql數(shù)據(jù)庫(kù)的方法,文中通過代碼示例介紹的非常詳細(xì),需要的朋友可以參考下2024-01-01
mysql 數(shù)據(jù)庫(kù)備份的多種實(shí)現(xiàn)方式總結(jié)
這篇文章主要介紹了mysql 數(shù)據(jù)庫(kù)備份的多種實(shí)現(xiàn)方式,總結(jié)分析了MySQL數(shù)據(jù)庫(kù)備份的常見語(yǔ)句、使用方法及操作注意事項(xiàng),需要的朋友可以參考下2020-02-02
MySQL不用like+%實(shí)現(xiàn)模糊查詢
本文主要介紹了MySQL不用like+%實(shí)現(xiàn)模糊查詢,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-01-01
mysql啟動(dòng)的error 2003和1067錯(cuò)誤問題解決方法
mysql安裝好經(jīng)常發(fā)現(xiàn)無(wú)法正常啟動(dòng)碰到最多的是error 2003的錯(cuò)誤,經(jīng)研究在此附上解決方法,希望可以幫助有類似問題的朋友2013-09-09

