Description:
The bug happens if `HAVE_GTID_NEXT_LIST` macro is specified, and if `thd->owned_gtid_set` has more than one gtid.
It cause mysqld to crash at `update_gtids_impl` function in the commit stage of binlog commiting.
This is because the `prev_sidno` in the follow code has not correct value, cause to `sid_lock` to locked repeatly;
And `sid_lock` also forget to be unlocked.
#ifdef HAVE_GTID_NEXT_LIST
rpl_sidno prev_sidno= 0;
Gtid_set::Gtid_iterator git(&thd->owned_gtid_set);
Gtid g= git.get();
while (g.sidno != 0)
{
if (g.sidno != prev_sidno)
sid_locks.lock(g.sidno);
owned_gtids.remove_gtid(g);
git.next();
g= git.get();
if (is_commit)
executed_gtids._add_gtid(g);
}
if (is_commit && !thd->owned_gtid_set.is_empty())
thd->rpl_thd_ctx.session_gtids_ctx().
notify_after_gtid_executed_update(thd);
thd->variables.gtid_next.set_undefined();
thd->owned_gtid.dbug_print(NULL,
"set owned_gtid (clear; old was gtid_set) "
"in update_gtids_impl");
thd->clear_owned_gtids();
#else
How to repeat:
1. compile mysqld with `HAVE_GTID_NEXT_LIST`
2. let `thd->owned_gtid_set` has more than one gtid
3. execute some sql statement that generate binlog
Suggested fix:
#ifdef HAVE_GTID_NEXT_LIST
rpl_sidno prev_sidno= 0;
Gtid_set::Gtid_iterator git(&thd->owned_gtid_set);
Gtid g= git.get();
while (g.sidno != 0)
{
if (g.sidno != prev_sidno)
sid_locks.lock(g.sidno);
owned_gtids.remove_gtid(g);
prev_sidno = g.sidno; // correct it
if (is_commit) {
executed_gtids._add_gtid(g);
if (thd->slave_thread && opt_bin_log && !opt_log_slave_updates)
{
lost_gtids._add_gtid(g);
gtids_only_in_table._add_gtid(g);
}
}
git.next();
g= git.get();
}
if (is_commit && !thd->owned_gtid_set.is_empty())
thd->rpl_thd_ctx.session_gtids_ctx().
notify_after_gtid_executed_update(thd);
broadcast_owned_sidnos(thd);
unlock_owned_sidnos(thd);
thd->clear_owned_gtids(); // unlock the sid
if (thd->variables.gtid_next.type == GTID_GROUP)
{
DBUG_ASSERT(!more_transactions_with_same_gtid_next);
thd->variables.gtid_next.set_undefined();
}
#else