/* Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "rpl_context.h" #include "rpl_gtid.h" // Gtid_set #include "sql_class.h" // THD Session_consistency_gtids_ctx::Session_consistency_gtids_ctx() : m_sid_map(NULL), m_gtid_set(NULL), m_listener(NULL), m_curr_session_track_gtids(OFF) { } Session_consistency_gtids_ctx::~Session_consistency_gtids_ctx() { if (m_gtid_set) { delete m_gtid_set; m_gtid_set= NULL; } if (m_sid_map) { delete m_sid_map; m_sid_map= NULL; } } inline bool Session_consistency_gtids_ctx::shall_collect(const THD* thd) { return /* Do not track OWN_GTID if session does not own a (non-anonymous) GTID. */ (thd->owned_gtid.sidno > 0 || m_curr_session_track_gtids == ALL_GTIDS) && /* if there is no listener/tracker, then there is no reason to collect */ m_listener != NULL && /* ROLLBACK statements may end up calling trans_commit_stmt */ thd->lex->sql_command != SQLCOM_ROLLBACK && thd->lex->sql_command != SQLCOM_ROLLBACK_TO_SAVEPOINT; } bool Session_consistency_gtids_ctx::notify_after_transaction_commit(const THD* thd) { DBUG_ENTER("Rpl_consistency_ctx::notify_after_transaction_commit"); DBUG_ASSERT(thd); bool res= false; if (!shall_collect(thd)) DBUG_RETURN(res); if (m_curr_session_track_gtids == ALL_GTIDS) { /* If one is configured to read all writes, we always collect the GTID_EXECUTED. NOTE: in the future optimize to collect deltas instead maybe. */ global_sid_lock->wrlock(); res= m_gtid_set->add_gtid_set(gtid_state->get_executed_gtids()) != RETURN_STATUS_OK; global_sid_lock->unlock(); if (!res) notify_ctx_change_listener(); } DBUG_RETURN(res); } bool Session_consistency_gtids_ctx::notify_after_gtid_executed_update(const THD *thd) { DBUG_ENTER("Rpl_consistency_ctx::notify_after_gtid_executed_update"); DBUG_ASSERT(thd); bool res= false; if (!shall_collect(thd)) DBUG_RETURN(res); if (m_curr_session_track_gtids == OWN_GTID) { DBUG_ASSERT(get_gtid_mode(GTID_MODE_LOCK_SID) != GTID_MODE_OFF); DBUG_ASSERT(thd->owned_gtid.sidno > 0); const Gtid& gtid= thd->owned_gtid; if (gtid.sidno == -1) // we need to add thd->owned_gtid_set { /* Caller must only call this function if the set was not empty. */ #ifdef HAVE_GTID_NEXT_LIST DBUG_ASSERT(!thd->owned_gtid_set.is_empty()); res= m_gtid_set->add_gtid_set(&thd->owned_gtid_set) != RETURN_STATUS_OK; #else DBUG_ASSERT(0); #endif } else if (gtid.sidno > 0) // only one gtid { /* Note that the interface is such that m_sid_map must contain sidno before we add the gtid to m_gtid_set. Thus, to avoid relying on global_sid_map and thus contributing to increased contention, we arrange for sidnos on the local sid map. */ rpl_sidno local_set_sidno= m_sid_map->add_sid(thd->owned_sid); DBUG_ASSERT(!m_gtid_set->contains_gtid(local_set_sidno, gtid.gno)); res= m_gtid_set->ensure_sidno(local_set_sidno) != RETURN_STATUS_OK; if (!res) m_gtid_set->_add_gtid(local_set_sidno, gtid.gno); } if (!res) notify_ctx_change_listener(); } DBUG_RETURN(res); } bool Session_consistency_gtids_ctx::notify_after_response_packet(const THD *thd) { int res= false; DBUG_ENTER("Rpl_consistency_ctx::notify_after_response_packet"); if (m_gtid_set && !m_gtid_set->is_empty()) m_gtid_set->clear(); /* Every time we get a notification that a packet was sent, we update this value. It may have changed (the previous command may have been a SET SESSION session_track_gtids=...;). */ m_curr_session_track_gtids= thd->variables.session_track_gtids; DBUG_RETURN(res); } void Session_consistency_gtids_ctx::register_ctx_change_listener( Session_consistency_gtids_ctx::Ctx_change_listener* listener, THD* thd) { DBUG_ASSERT(m_listener == NULL || m_listener == listener); if (m_listener == NULL) { DBUG_ASSERT(m_sid_map == NULL && m_gtid_set == NULL); m_listener= listener; m_sid_map= new Sid_map(NULL); m_gtid_set= new Gtid_set(m_sid_map); /* Caches the value at startup if needed. This is called during THD::init, if the session_track_gtids value is set at startup time to anything different than OFF. */ m_curr_session_track_gtids= thd->variables.session_track_gtids; } } void Session_consistency_gtids_ctx::unregister_ctx_change_listener( Session_consistency_gtids_ctx::Ctx_change_listener* listener) { DBUG_ASSERT(m_listener == listener || m_listener == NULL); if (m_gtid_set) delete m_gtid_set; if (m_sid_map) delete m_sid_map; m_listener= NULL; m_gtid_set= NULL; m_sid_map= NULL; }