1/***************************************************************************** 2 3Copyright (c) 1996, 2021, Oracle and/or its affiliates. 4 5This program is free software; you can redistribute it and/or modify 6it under the terms of the GNU General Public License, version 2.0, 7as published by the Free Software Foundation. 8 9This program is also distributed with certain software (including 10but not limited to OpenSSL) that is licensed under separate terms, 11as designated in a particular file or component or in included license 12documentation. The authors of MySQL hereby grant you an additional 13permission to link the program and your derivative works with the 14separately licensed software that they have included with MySQL. 15 16This program is distributed in the hope that it will be useful, 17but WITHOUT ANY WARRANTY; without even the implied warranty of 18MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19GNU General Public License, version 2.0, for more details. 20 21You should have received a copy of the GNU General Public License along with 22this program; if not, write to the Free Software Foundation, Inc., 2351 Franklin Street, Suite 500, Boston, MA 02110-1335 USA 24 25*****************************************************************************/ 26 27/**************************************************//** 28@file include/trx0trx.ic 29The transaction 30 31Created 3/26/1996 Heikki Tuuri 32*******************************************************/ 33 34#include "read0read.h" 35 36/**********************************************************************//** 37Determines if a transaction is in the given state. 38The caller must hold trx_sys->mutex, or it must be the thread 39that is serving a running transaction. 40A running RW transaction must be in trx_sys->rw_trx_list. 41@return TRUE if trx->state == state */ 42UNIV_INLINE 43bool 44trx_state_eq( 45/*=========*/ 46 const trx_t* trx, /*!< in: transaction */ 47 trx_state_t state) /*!< in: state */ 48{ 49#ifdef UNIV_DEBUG 50 switch (trx->state) { 51 case TRX_STATE_PREPARED: 52 53 ut_ad(!trx_is_autocommit_non_locking(trx)); 54 return(trx->state == state); 55 56 case TRX_STATE_ACTIVE: 57 58 assert_trx_nonlocking_or_in_list(trx); 59 return(state == trx->state); 60 61 case TRX_STATE_COMMITTED_IN_MEMORY: 62 63 check_trx_state(trx); 64 return(state == trx->state); 65 66 case TRX_STATE_NOT_STARTED: 67 case TRX_STATE_FORCED_ROLLBACK: 68 69 /* These states are not allowed for running transactions. */ 70 ut_a(state == TRX_STATE_NOT_STARTED 71 || state == TRX_STATE_FORCED_ROLLBACK); 72 73 ut_ad(!trx->in_rw_trx_list); 74 75 return(true); 76 } 77 ut_error; 78#endif /* UNIV_DEBUG */ 79 return(trx->state == state); 80} 81 82/****************************************************************//** 83Retrieves the index causing the error from a trx. 84@return the error info */ 85UNIV_INLINE 86const dict_index_t* 87trx_get_error_index( 88/*===============*/ 89 const trx_t* trx) /*!< in: trx object */ 90{ 91 return(trx->error_index); 92} 93 94/*******************************************************************//** 95Retrieves transaction's que state in a human readable string. The string 96should not be free()'d or modified. 97@return string in the data segment */ 98UNIV_INLINE 99const char* 100trx_get_que_state_str( 101/*==================*/ 102 const trx_t* trx) /*!< in: transaction */ 103{ 104 /* be sure to adjust TRX_QUE_STATE_STR_MAX_LEN if you change this */ 105 switch (trx->lock.que_state) { 106 case TRX_QUE_RUNNING: 107 return("RUNNING"); 108 case TRX_QUE_LOCK_WAIT: 109 return("LOCK WAIT"); 110 case TRX_QUE_ROLLING_BACK: 111 return("ROLLING BACK"); 112 case TRX_QUE_COMMITTING: 113 return("COMMITTING"); 114 default: 115 return("UNKNOWN"); 116 } 117} 118 119/** Retreieves the transaction ID. 120In a given point in time it is guaranteed that IDs of the running 121transactions are unique. The values returned by this function for readonly 122transactions may be reused, so a subsequent RO transaction may get the same ID 123as a RO transaction that existed in the past. The values returned by this 124function should be used for printing purposes only. 125@param[in] trx transaction whose id to retrieve 126@return transaction id */ 127UNIV_INLINE 128trx_id_t 129trx_get_id_for_print( 130 const trx_t* trx) 131{ 132 /* Readonly and transactions whose intentions are unknown (whether 133 they will eventually do a WRITE) don't have trx_t::id assigned (it is 134 0 for those transactions). Transaction IDs in 135 innodb_trx.trx_id, 136 innodb_locks.lock_id, 137 innodb_locks.lock_trx_id, 138 innodb_lock_waits.requesting_trx_id, 139 innodb_lock_waits.blocking_trx_id should match because those tables 140 could be used in an SQL JOIN on those columns. Also trx_t::id is 141 printed by SHOW ENGINE INNODB STATUS, and in logs, so we must have the 142 same value printed everywhere consistently. */ 143 144 /* DATA_TRX_ID_LEN is the storage size in bytes. */ 145 static const trx_id_t max_trx_id 146 = (1ULL << (DATA_TRX_ID_LEN * CHAR_BIT)) - 1; 147 148 ut_ad(trx->id <= max_trx_id); 149 150 return(trx->id != 0 151 ? trx->id 152 : reinterpret_cast<trx_id_t>(trx) | (max_trx_id + 1)); 153} 154 155/**********************************************************************//** 156Determine if a transaction is a dictionary operation. 157@return dictionary operation mode */ 158UNIV_INLINE 159enum trx_dict_op_t 160trx_get_dict_operation( 161/*===================*/ 162 const trx_t* trx) /*!< in: transaction */ 163{ 164 trx_dict_op_t op = static_cast<trx_dict_op_t>(trx->dict_operation); 165 166#ifdef UNIV_DEBUG 167 switch (op) { 168 case TRX_DICT_OP_NONE: 169 case TRX_DICT_OP_TABLE: 170 case TRX_DICT_OP_INDEX: 171 return(op); 172 } 173 ut_error; 174#endif /* UNIV_DEBUG */ 175 return(op); 176} 177/**********************************************************************//** 178Flag a transaction a dictionary operation. */ 179UNIV_INLINE 180void 181trx_set_dict_operation( 182/*===================*/ 183 trx_t* trx, /*!< in/out: transaction */ 184 enum trx_dict_op_t op) /*!< in: operation, not 185 TRX_DICT_OP_NONE */ 186{ 187#ifdef UNIV_DEBUG 188 enum trx_dict_op_t old_op = trx_get_dict_operation(trx); 189 190 switch (op) { 191 case TRX_DICT_OP_NONE: 192 ut_error; 193 break; 194 case TRX_DICT_OP_TABLE: 195 switch (old_op) { 196 case TRX_DICT_OP_NONE: 197 case TRX_DICT_OP_INDEX: 198 case TRX_DICT_OP_TABLE: 199 goto ok; 200 } 201 ut_error; 202 break; 203 case TRX_DICT_OP_INDEX: 204 ut_ad(old_op == TRX_DICT_OP_NONE); 205 break; 206 } 207ok: 208#endif /* UNIV_DEBUG */ 209 210 trx->ddl = true; 211 trx->dict_operation = op; 212} 213 214/** 215Releases the search latch if trx has reserved it. 216@param[in,out] trx Transaction that may own the AHI latch */ 217UNIV_INLINE 218void 219trx_search_latch_release_if_reserved(trx_t* trx) 220{ 221 ut_a(!trx->has_search_latch); 222} 223 224/********************************************************************//** 225Check if redo rseg is modified for insert/update. */ 226UNIV_INLINE 227bool 228trx_is_redo_rseg_updated( 229/*=====================*/ 230 const trx_t* trx) /*!< in: transaction */ 231{ 232 return(trx->rsegs.m_redo.insert_undo != 0 233 || trx->rsegs.m_redo.update_undo != 0); 234} 235 236/********************************************************************//** 237Check if noredo rseg is modified for insert/update. */ 238UNIV_INLINE 239bool 240trx_is_noredo_rseg_updated( 241/*=======================*/ 242 const trx_t* trx) /*!< in: transaction */ 243{ 244 return(trx->rsegs.m_noredo.insert_undo != 0 245 || trx->rsegs.m_noredo.update_undo != 0); 246} 247 248/********************************************************************//** 249Check if redo/noredo rseg is modified for insert/update. */ 250UNIV_INLINE 251bool 252trx_is_rseg_updated( 253/*================*/ 254 const trx_t* trx) /*!< in: transaction */ 255{ 256 return(trx_is_redo_rseg_updated(trx) 257 || trx_is_noredo_rseg_updated(trx)); 258} 259 260/********************************************************************//** 261Check if redo/nonredo rseg is valid. */ 262UNIV_INLINE 263bool 264trx_is_rseg_assigned( 265/*=================*/ 266 const trx_t* trx) /*!< in: transaction */ 267{ 268 return(trx->rsegs.m_redo.rseg != NULL 269 || trx->rsegs.m_noredo.rseg != NULL); 270} 271 272/** 273Increase the reference count. If the transaction is in state 274TRX_STATE_COMMITTED_IN_MEMORY then the transaction is considered 275committed and the reference count is not incremented. 276@param trx Transaction that is being referenced 277@param do_ref_count Increment the reference iff this is true 278@return transaction instance if it is not committed */ 279UNIV_INLINE 280trx_t* 281trx_reference( 282 trx_t* trx, 283 bool do_ref_count) 284{ 285 trx_mutex_enter(trx); 286 287 if (trx_state_eq(trx, TRX_STATE_COMMITTED_IN_MEMORY)) { 288 trx_mutex_exit(trx); 289 trx = NULL; 290 } else if (do_ref_count) { 291 ut_ad(trx->n_ref >= 0); 292 ++trx->n_ref; 293 trx_mutex_exit(trx); 294 } else { 295 trx_mutex_exit(trx); 296 } 297 298 return(trx); 299} 300 301/** 302Release the transaction. Decrease the reference count. 303@param trx Transaction that is being released */ 304UNIV_INLINE 305void 306trx_release_reference( 307 trx_t* trx) 308{ 309 trx_mutex_enter(trx); 310 311 ut_ad(trx->n_ref > 0); 312 --trx->n_ref; 313 314 trx_mutex_exit(trx); 315} 316 317 318/** 319@param trx Get the active view for this transaction, if one exists 320@return the transaction's read view or NULL if one not assigned. */ 321UNIV_INLINE 322ReadView* 323trx_get_read_view( 324 trx_t* trx) 325{ 326 return(!MVCC::is_view_active(trx->read_view) ? NULL : trx->read_view); 327} 328 329/** 330@param trx Get the active view for this transaction, if one exists 331@return the transaction's read view or NULL if one not assigned. */ 332UNIV_INLINE 333const ReadView* 334trx_get_read_view( 335 const trx_t* trx) 336{ 337 return(!MVCC::is_view_active(trx->read_view) ? NULL : trx->read_view); 338} 339 340/** 341@param[in] trx Transaction to check 342@return true if the transaction is a high priority transaction.*/ 343UNIV_INLINE 344bool 345trx_is_high_priority(const trx_t* trx) 346{ 347 if (trx->mysql_thd == NULL) { 348 return(false); 349 } 350 351 return(thd_trx_priority(trx->mysql_thd) > 0); 352} 353 354/** 355@param[in] requestor Transaction requesting the lock 356@param[in] holder Transaction holding the lock 357@return the transaction that will be rolled back, null don't care */ 358UNIV_INLINE 359const trx_t* 360trx_arbitrate(const trx_t* requestor, const trx_t* holder) 361{ 362 ut_ad(!trx_is_autocommit_non_locking(holder)); 363 ut_ad(!trx_is_autocommit_non_locking(requestor)); 364 365 /* Note: Background stats collection transactions also acquire 366 locks on user tables. They don't have an associated MySQL session 367 instance. */ 368 369 if (requestor->mysql_thd == NULL) { 370 371 ut_ad(!trx_is_high_priority(requestor)); 372 373 if (trx_is_high_priority(holder)) { 374 return(requestor); 375 } else { 376 return(NULL); 377 } 378 379 } else if (holder->mysql_thd == NULL) { 380 381 ut_ad(!trx_is_high_priority(holder)); 382 383 if (trx_is_high_priority(requestor)) { 384 return(holder); 385 } 386 387 return(NULL); 388 } 389 390 const THD* victim = thd_trx_arbitrate( 391 requestor->mysql_thd, holder->mysql_thd); 392 393 ut_ad(victim == NULL 394 || victim == requestor->mysql_thd 395 || victim == holder->mysql_thd); 396 397 if (victim != NULL) { 398 return(victim == requestor->mysql_thd ? requestor : holder); 399 } 400 401 return(NULL); 402} 403