源碼分析丨MySQL的多層SP中Cursor相關(guān)BUG
一、問題發(fā)現(xiàn)
在一次開發(fā)中在sp中使用多層cursor的時候想知道每層的m_max_cursor_index
值分別是多少,以用來做后續(xù)開發(fā)。于是做了以下的試驗,但是發(fā)現(xiàn)第一個level=2那層的m_max_cursor_index
的值有點問題。
注:本次使用的MySQL數(shù)據(jù)庫版本為最新的debug版本。
SQL語句示例:
greatsql> CREATE TABLE t1 (a INT, b VARCHAR(10));
以下注釋里面是該層sp_pcontext的參數(shù)值。
DELIMITER $$
CREATE PROCEDURE processnames() -- level=0,m_max_cursor_index=1+8+1
BEGIN
DECLARE nameCursor0 CURSOR FOR SELECT * FROM t1; -- level=1,m_cursor_offset=0,m_max_cursor_index=1+8+1
begin
DECLARE nameCursor1 CURSOR FOR SELECT * FROM t1; -- level=2,m_cursor_offset=1,m_max_cursor_index=1+8 ☆問題點
begin
DECLARE nameCursor2 CURSOR FOR SELECT * FROM t1; -- level=3,m_cursor_offset=2,m_max_cursor_index=1
DECLARE nameCursor3 CURSOR FOR SELECT * FROM t1; -- level=3,m_cursor_offset=2,m_max_cursor_index=2
DECLARE nameCursor4 CURSOR FOR SELECT * FROM t1; -- level=3,m_cursor_offset=2,m_max_cursor_index=3
DECLARE nameCursor5 CURSOR FOR SELECT * FROM t1; -- level=3,m_cursor_offset=2,m_max_cursor_index=4
end;
end;
begin
DECLARE nameCursor6 CURSOR FOR SELECT * FROM t1; -- level=2,m_cursor_offset=1,m_max_cursor_index=1
end;
END $$
DELIMITER ;
首先查看上面的sp的code,可以發(fā)現(xiàn)nameCursor6
和nameCursor1
屬于同一層,因此他們的offset值一樣。
greatsql> show procedure code processnames;
+-----+---------------------------------------+
| Pos | Instruction |
+-----+---------------------------------------+
| 0 | cpush nameCursor0@0: SELECT * FROM t1 |
| 1 | cpush nameCursor1@1: SELECT * FROM t1 |
| 2 | cpush nameCursor2@2: SELECT * FROM t1 |
| 3 | cpush nameCursor3@3: SELECT * FROM t1 |
| 4 | cpush nameCursor4@4: SELECT * FROM t1 |
| 5 | cpush nameCursor5@5: SELECT * FROM t1 |
| 6 | cpop 4 |
| 7 | cpop 1 |
| 8 | cpush nameCursor6@1: SELECT * FROM t1 |
| 9 | cpop 1 |
| 10 | cpop 1 |
+-----+---------------------------------------+
11 rows in set (6.02 sec)
然后通過debug查看每層sp_pcontext的參數(shù)值(相關(guān)參數(shù)值已經(jīng)在上面標識出),發(fā)現(xiàn)第一個level=2的sp_pcontext的m_max_cursor_index
值多了很多,預期值應該是4+1,但是實際是8+1,而上面的層都沒錯,這說明代碼最里面那層m_max_cursor_index
賦值錯了。
二、問題調(diào)查過程
1、發(fā)現(xiàn)了問題點就看看代碼里面對于每層的m_max_cursor_index
是怎么賦值的。
1、初始化sp_pcontext的時候所有的參數(shù)都為0
sp_pcontext::sp_pcontext(THD *thd)
: m_level(0),
m_max_var_index(0),
m_max_cursor_index(0)...{init(0, 0, 0, 0);}
2、每加一層sp_pcontext,當前的m_cursor_offset=上一層cursor個數(shù)
sp_pcontext::sp_pcontext(THD *thd, sp_pcontext *prev,
sp_pcontext::enum_scope scope)
: m_level(prev->m_level + 1),
m_max_var_index(0),
m_max_cursor_index(0)... {init(prev->current_cursor_count());}
void sp_pcontext::init(uint cursor_offset) {m_cursor_offset = cursor_offset;}
uint current_cursor_count() const {
return m_cursor_offset + static_cast<uint>(m_cursors.size());
}
3、退出當前sp_pcontext層,需要把當前的max_cursor_index()信息值賦值給上一層的m_max_cursor_index,即當前的cursor數(shù)量累加給上一層
sp_pcontext *sp_pcontext::pop_context() {
uint submax = max_cursor_index();
if (submax > m_parent->m_max_cursor_index)
m_parent->m_max_cursor_index = submax;
}
uint max_cursor_index() const {
return m_max_cursor_index + static_cast<uint>(m_cursors.size());
}
4、每次增加一個cursor,m_max_cursor_index值遞增,m_max_cursor_index是計數(shù)器。
bool sp_pcontext::add_cursor(LEX_STRING name) {
if (m_cursors.size() == m_max_cursor_index) ++m_max_cursor_index;
return m_cursors.push_back(name);
}
2、根據(jù)第一步的分析,只在最里面那層的m_max_cursor_index
累加出來計算錯誤,看看上面的累加過程,是用max_cursor_index()
值來累加的,于是查看max_cursor_index()
函數(shù)的實現(xiàn):
uint max_cursor_index() const {
return m_max_cursor_index + static_cast<uint>(m_cursors.size());
}
這里是把當前層的m_max_cursor_index
值加上m_cursors.size()
,但是在函數(shù)add_cursor
里面,m_cursors
數(shù)組每增加一個cursor
,m_max_cursor_index
都要加1,也就是說在最里面那層sp_pcontext
的計算重復了,計算了2遍m_cursors.size()
,導致上面的level=2那層的m_max_cursor_index
值變成2*4=8了。到這里問題點發(fā)現(xiàn)。
三、問題解決方案
通過以上代碼解析后,可以考慮只對最里面那層sp_pcontext
的max_cursor_index()
取值進行修改,最里面那層的sp_pcontext
沒有m_children
,因此可以用這個數(shù)組值進行判斷。代碼作如下修改:
uint max_cursor_index() const {
if(m_children.size() == 0) -- 最里面那層sp_pcontext直接返回m_max_cursor_index的值。
return m_max_cursor_index; -- 可以改為static_cast<uint>(m_cursors.size()),二者值一樣。
else -- 上層sp_pcontext返回下層所有sp_pcontext的m_max_cursor_index的值,再加上當前層的m_cursors.size()值。
return m_max_cursor_index + static_cast<uint>(m_cursors.size());
}
四、問題總結(jié)
在MySQL的sp里面使用cursor的話,因為m_max_cursor_index
只用于統(tǒng)計,不用于實際賦值和計算過程,因此不影響使用。但是如果要用這個值用于二次開發(fā),就要注意到這個問題。上面的修改方案只是其中一個解決方案,也可以根據(jù)自己的需要去改add_cursor的m_max_cursor_index
的賦值過程。
這次發(fā)現(xiàn)的問題屬于不參與計算的bug,但卻影響開源代碼的后續(xù)開發(fā),在實際開發(fā)應用中類似的問題也要注意,一不小心就會踩坑。
Enjoy GreatSQL ??
關(guān)于 GreatSQL
GreatSQL是適用于金融級應用的國內(nèi)自主開源數(shù)據(jù)庫,具備高性能、高可靠、高易用性、高安全等多個核心特性,可以作為MySQL或Percona Server的可選替換,用于線上生產(chǎn)環(huán)境,且完全免費并兼容MySQL或Percona Server。
相關(guān)鏈接: GreatSQL社區(qū) Gitee GitHub Bilibili
GreatSQL社區(qū):
社區(qū)博客有獎征稿詳情:https://greatsql.cn/thread-100-1-1.html
技術(shù)交流群:
微信:掃碼添加
GreatSQL社區(qū)助手
微信好友,發(fā)送驗證信息加群
。文章來源:http://www.zghlxwxcb.cn/news/detail-844301.html
文章來源地址http://www.zghlxwxcb.cn/news/detail-844301.html
到了這里,關(guān)于MySQL的多層SP中Cursor的m_max_cursor_index相關(guān)BUG分析的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!