1 /* Copyright (c) 2000, 2021, Oracle and/or its affiliates. 2 3 This program is free software; you can redistribute it and/or modify 4 it under the terms of the GNU General Public License, version 2.0, 5 as published by the Free Software Foundation. 6 7 This program is also distributed with certain software (including 8 but not limited to OpenSSL) that is licensed under separate terms, 9 as designated in a particular file or component or in included license 10 documentation. The authors of MySQL hereby grant you an additional 11 permission to link the program and your derivative works with the 12 separately licensed software that they have included with MySQL. 13 14 This program is distributed in the hope that it will be useful, 15 but WITHOUT ANY WARRANTY; without even the implied warranty of 16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 GNU General Public License, version 2.0, for more details. 18 19 You should have received a copy of the GNU General Public License 20 along with this program; if not, write to the Free Software 21 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ 22 23 #ifndef TRANSACTION_INFO_INCLUDED 24 #define TRANSACTION_INFO_INCLUDED 25 26 #include "my_global.h" 27 #include "my_dbug.h" // DBUG_ENTER 28 #include "my_sys.h" // strmake_root 29 #include "xa.h" // XID_STATE 30 #include "my_alloc.h" // MEM_ROOT 31 #include "thr_malloc.h" // init_sql_alloc 32 #include "sql_cache.h" // query_cache 33 #include "mdl.h" // MDL_savepoint 34 #include "handler.h" // handlerton 35 #include "rpl_transaction_ctx.h" // Rpl_transaction_ctx 36 #include "rpl_transaction_write_set_ctx.h" // Transaction_write_set_ctx 37 38 class THD; 39 40 typedef struct st_changed_table_list 41 { 42 struct st_changed_table_list *next; 43 char *key; 44 uint32 key_length; 45 } CHANGED_TABLE_LIST; 46 47 48 /** 49 Either statement transaction or normal transaction - related 50 thread-specific storage engine data. 51 52 If a storage engine participates in a statement/transaction, 53 an instance of this class is present in 54 thd->m_transaction.m_scope_info[STMT|SESSION].ha_list. The addition 55 this list is made by trans_register_ha(). 56 57 When it's time to commit or rollback, each element of ha_list 58 is used to access storage engine's prepare()/commit()/rollback() 59 methods, and also to evaluate if a full two phase commit is 60 necessary. 61 62 @sa General description of transaction handling in handler.cc. 63 */ 64 65 class Ha_trx_info 66 { 67 public: 68 69 /** 70 Register this storage engine in the given transaction context. 71 */ 72 register_ha(Ha_trx_info * ha_info,handlerton * ht_arg)73 void register_ha(Ha_trx_info *ha_info, handlerton *ht_arg) 74 { 75 DBUG_ENTER("Ha_trx_info::register_ha"); 76 DBUG_PRINT("enter", ("ht: 0x%llx (%s)", 77 (ulonglong) ht_arg, 78 ha_legacy_type_name(ht_arg->db_type))); 79 assert(m_flags == 0); 80 assert(m_ht == NULL); 81 assert(m_next == NULL); 82 83 m_ht= ht_arg; 84 m_flags= (int) TRX_READ_ONLY; /* Assume read-only at start. */ 85 86 m_next= ha_info; 87 88 DBUG_VOID_RETURN; 89 } 90 91 /** 92 Clear, prepare for reuse. 93 */ 94 reset()95 void reset() 96 { 97 DBUG_ENTER("Ha_trx_info::reset"); 98 m_next= NULL; 99 m_ht= NULL; 100 m_flags= 0; 101 DBUG_VOID_RETURN; 102 } 103 Ha_trx_info()104 Ha_trx_info() 105 { 106 reset(); 107 } 108 set_trx_read_write()109 void set_trx_read_write() 110 { 111 assert(is_started()); 112 m_flags|= (int) TRX_READ_WRITE; 113 } 114 is_trx_read_write()115 bool is_trx_read_write() const 116 { 117 assert(is_started()); 118 return m_flags & (int) TRX_READ_WRITE; 119 } 120 121 /** 122 Set the transaction flag to noop_read_write 123 If the transaction has no operation dml statement. 124 */ set_trx_noop_read_write()125 void set_trx_noop_read_write() 126 { 127 assert(is_started()); 128 m_flags|= (int) TRX_NOOP_READ_WRITE; 129 } 130 131 /** 132 Check if the stmt transaction has noop_read_write flag set. 133 */ is_trx_noop_read_write()134 bool is_trx_noop_read_write() const 135 { 136 assert(is_started()); 137 return m_flags & (int) TRX_NOOP_READ_WRITE; 138 } 139 is_started()140 bool is_started() const 141 { 142 return m_ht != NULL; 143 } 144 145 146 /** 147 Mark this transaction read-write if the argument is read-write. 148 */ 149 coalesce_trx_with(const Ha_trx_info * stmt_trx)150 void coalesce_trx_with(const Ha_trx_info *stmt_trx) 151 { 152 /* 153 Must be called only after the transaction has been started. 154 Can be called many times, e.g. when we have many 155 read-write statements in a transaction. 156 */ 157 assert(is_started()); 158 if (stmt_trx->is_trx_read_write()) 159 set_trx_read_write(); 160 if (stmt_trx->is_trx_noop_read_write()) 161 set_trx_noop_read_write(); 162 } 163 next()164 Ha_trx_info *next() const 165 { 166 assert(is_started()); 167 return m_next; 168 } 169 ht()170 handlerton *ht() const 171 { 172 assert(is_started()); 173 return m_ht; 174 } 175 176 private: 177 enum { TRX_READ_ONLY= 0, TRX_READ_WRITE= 1, TRX_NOOP_READ_WRITE= 2 }; 178 /** 179 Auxiliary, used for ha_list management 180 */ 181 Ha_trx_info *m_next; 182 183 /** 184 Although a given Ha_trx_info instance is currently always used 185 for the same storage engine, 'ht' is not-NULL only when the 186 corresponding storage is a part of a transaction. 187 */ 188 handlerton *m_ht; 189 190 /** 191 Transaction flags related to this engine. 192 Not-null only if this instance is a part of transaction. 193 May assume a combination of enum values above. 194 */ 195 uchar m_flags; 196 }; 197 198 struct st_savepoint 199 { 200 struct st_savepoint *prev; 201 char *name; 202 size_t length; 203 Ha_trx_info *ha_list; 204 /** State of metadata locks before this savepoint was set. */ 205 MDL_savepoint mdl_savepoint; 206 }; 207 208 class Transaction_ctx 209 { 210 public: 211 enum enum_trx_scope { STMT= 0, SESSION }; 212 213 SAVEPOINT *m_savepoints; 214 215 private: 216 struct THD_TRANS 217 { 218 /* true is not all entries in the ht[] support 2pc */ 219 bool m_no_2pc; 220 int m_rw_ha_count; 221 /* storage engines that registered in this transaction */ 222 Ha_trx_info *m_ha_list; 223 224 private: 225 /* 226 The purpose of this member variable (i.e. flag) is to keep track of 227 statements which cannot be rolled back safely(completely). 228 For example, 229 230 * statements that modified non-transactional tables. The value 231 MODIFIED_NON_TRANS_TABLE is set within mysql_insert, mysql_update, 232 mysql_delete, etc if a non-transactional table is modified. 233 234 * 'DROP TEMPORARY TABLE' and 'CREATE TEMPORARY TABLE' statements. 235 The former sets the value DROPPED_TEMP_TABLE and the latter 236 the value CREATED_TEMP_TABLE. 237 238 The tracked statements are modified in scope of: 239 240 * transaction, when the variable is a member of 241 THD::m_transaction.m_scope_info[SESSION] 242 243 * top-level statement or sub-statement, when the variable is a 244 member of THD::m_transaction.m_scope_info[STMT] 245 246 This member has the following life cycle: 247 248 * m_scope_info[STMT].m_unsafe_rollback_flags is used to keep track of 249 top-level statements which cannot be rolled back safely. At the end of the 250 statement, the value of m_scope_info[STMT].m_unsafe_rollback_flags is 251 merged with m_scope_info[SESSION].m_unsafe_rollback_flags 252 and gets reset. 253 254 * m_scope_info[SESSION].cannot_safely_rollback is reset at the end 255 of transaction 256 257 * Since we do not have a dedicated context for execution of 258 a sub-statement, to keep track of non-transactional changes in a 259 sub-statement, we re-use m_scope_info[STMT].m_unsafe_rollback_flags. 260 At entrance into a sub-statement, a copy of the value of 261 m_scope_info[STMT].m_unsafe_rollback_flags (containing the changes of the 262 outer statement) is saved on stack. 263 Then m_scope_info[STMT].m_unsafe_rollback_flags is reset to 0 and the 264 substatement is executed. Then the new value is merged 265 with the saved value. 266 */ 267 268 unsigned int m_unsafe_rollback_flags; 269 /* 270 Define the type of statements which cannot be rolled back safely. 271 Each type occupies one bit in m_unsafe_rollback_flags. 272 */ 273 static unsigned int const MODIFIED_NON_TRANS_TABLE= 0x01; 274 static unsigned int const CREATED_TEMP_TABLE= 0x02; 275 static unsigned int const DROPPED_TEMP_TABLE= 0x04; 276 277 public: cannot_safely_rollbackTHD_TRANS278 bool cannot_safely_rollback() const 279 { 280 return m_unsafe_rollback_flags > 0; 281 } get_unsafe_rollback_flagsTHD_TRANS282 unsigned int get_unsafe_rollback_flags() const 283 { 284 return m_unsafe_rollback_flags; 285 } set_unsafe_rollback_flagsTHD_TRANS286 void set_unsafe_rollback_flags(unsigned int flags) 287 { 288 DBUG_PRINT("debug", ("set_unsafe_rollback_flags: %d", flags)); 289 m_unsafe_rollback_flags= flags; 290 } add_unsafe_rollback_flagsTHD_TRANS291 void add_unsafe_rollback_flags(unsigned int flags) 292 { 293 DBUG_PRINT("debug", ("add_unsafe_rollback_flags: %d", flags)); 294 m_unsafe_rollback_flags|= flags; 295 } reset_unsafe_rollback_flagsTHD_TRANS296 void reset_unsafe_rollback_flags() 297 { 298 DBUG_PRINT("debug", ("reset_unsafe_rollback_flags")); 299 m_unsafe_rollback_flags= 0; 300 } mark_modified_non_trans_tableTHD_TRANS301 void mark_modified_non_trans_table() 302 { 303 DBUG_PRINT("debug", ("mark_modified_non_trans_table")); 304 m_unsafe_rollback_flags|= MODIFIED_NON_TRANS_TABLE; 305 } has_modified_non_trans_tableTHD_TRANS306 bool has_modified_non_trans_table() const 307 { 308 return m_unsafe_rollback_flags & MODIFIED_NON_TRANS_TABLE; 309 } mark_created_temp_tableTHD_TRANS310 void mark_created_temp_table() 311 { 312 DBUG_PRINT("debug", ("mark_created_temp_table")); 313 m_unsafe_rollback_flags|= CREATED_TEMP_TABLE; 314 } has_created_temp_tableTHD_TRANS315 bool has_created_temp_table() const 316 { 317 return m_unsafe_rollback_flags & CREATED_TEMP_TABLE; 318 } mark_dropped_temp_tableTHD_TRANS319 void mark_dropped_temp_table() 320 { 321 DBUG_PRINT("debug", ("mark_dropped_temp_table")); 322 m_unsafe_rollback_flags|= DROPPED_TEMP_TABLE; 323 } has_dropped_temp_tableTHD_TRANS324 bool has_dropped_temp_table() const 325 { 326 return m_unsafe_rollback_flags & DROPPED_TEMP_TABLE; 327 } 328 resetTHD_TRANS329 void reset() 330 { 331 m_no_2pc= false; 332 m_rw_ha_count= 0; 333 reset_unsafe_rollback_flags(); 334 } is_emptyTHD_TRANS335 bool is_empty() const { return m_ha_list == NULL; } 336 }; 337 338 THD_TRANS m_scope_info[2]; 339 340 XID_STATE m_xid_state; 341 342 /* 343 Tables changed in transaction (that must be invalidated in query cache). 344 List contain only transactional tables, that not invalidated in query 345 cache (instead of full list of changed in transaction tables). 346 */ 347 CHANGED_TABLE_LIST* m_changed_tables; 348 MEM_ROOT m_mem_root; // Transaction-life memory allocation pool 349 350 public: 351 /* 352 (Mostly) binlog-specific fields use while flushing the caches 353 and committing transactions. 354 We don't use bitfield any more in the struct. Modification will 355 be lost when concurrently updating multiple bit fields. It will 356 cause a race condition in a multi-threaded application. And we 357 already caught a race condition case between xid_written and 358 ready_preempt in MYSQL_BIN_LOG::ordered_commit. 359 */ 360 struct 361 { 362 bool enabled; // see ha_enable_transaction() 363 bool pending; // Is the transaction commit pending? 364 bool xid_written; // The session wrote an XID 365 bool real_commit; // Is this a "real" commit? 366 bool commit_low; // see MYSQL_BIN_LOG::ordered_commit 367 bool run_hooks; // Call the after_commit hook 368 #ifndef NDEBUG 369 bool ready_preempt; // internal in MYSQL_BIN_LOG::ordered_commit 370 #endif 371 } m_flags; 372 /* Binlog-specific logical timestamps. */ 373 /* 374 Store for the transaction's commit parent sequence_number. 375 The value specifies this transaction dependency with a "parent" 376 transaction. 377 The member is assigned, when the transaction is about to commit 378 in binlog to a value of the last committed transaction's sequence_number. 379 This and last_committed as numbers are kept ever incremented 380 regardless of binary logs being rotated or when transaction 381 is logged in multiple pieces. 382 However the logger to the binary log may convert them 383 according to its specification. 384 */ 385 int64 last_committed; 386 /* 387 The transaction's private logical timestamp assigned at the 388 transaction prepare phase. The timestamp enumerates transactions 389 in the binary log. The value is gained through incrementing (stepping) a 390 global clock. 391 Eventually the value is considered to increase max_committed_transaction 392 system clock when the transaction has committed. 393 */ 394 int64 sequence_number; 395 store_commit_parent(int64 last_arg)396 void store_commit_parent(int64 last_arg) 397 { 398 last_committed= last_arg; 399 } 400 401 Transaction_ctx(); ~Transaction_ctx()402 virtual ~Transaction_ctx() 403 { 404 free_root(&m_mem_root, MYF(0)); 405 } 406 cleanup()407 void cleanup() 408 { 409 DBUG_ENTER("Transaction_ctx::cleanup"); 410 m_changed_tables= NULL; 411 m_savepoints= NULL; 412 m_xid_state.cleanup(); 413 m_rpl_transaction_ctx.cleanup(); 414 m_transaction_write_set_ctx.reset_state(); 415 free_root(&m_mem_root,MYF(MY_KEEP_PREALLOC)); 416 DBUG_VOID_RETURN; 417 } 418 is_active(enum_trx_scope scope)419 bool is_active(enum_trx_scope scope) const 420 { 421 return m_scope_info[scope].m_ha_list != NULL; 422 } 423 424 void push_unsafe_rollback_warnings(THD *thd); 425 merge_unsafe_rollback_flags()426 void merge_unsafe_rollback_flags() 427 { 428 /* 429 Merge m_scope_info[STMT].unsafe_rollback_flags to 430 m_scope_info[SESSION].unsafe_rollback_flags. If the statement 431 cannot be rolled back safely, the transaction including 432 this statement definitely cannot rolled back safely. 433 */ 434 m_scope_info[SESSION].add_unsafe_rollback_flags( 435 m_scope_info[STMT].get_unsafe_rollback_flags()); 436 } 437 init_mem_root_defaults(ulong trans_alloc_block_size,ulong trans_prealloc_size)438 void init_mem_root_defaults(ulong trans_alloc_block_size, 439 ulong trans_prealloc_size) 440 { 441 reset_root_defaults(&m_mem_root, 442 trans_alloc_block_size, 443 trans_prealloc_size); 444 } 445 transaction_memroot()446 MEM_ROOT* transaction_memroot() 447 { 448 return &m_mem_root; 449 } 450 allocate_memory(unsigned int size)451 void* allocate_memory(unsigned int size) 452 { 453 return alloc_root(&m_mem_root, size); 454 } 455 claim_memory_ownership()456 void claim_memory_ownership() 457 { 458 claim_root(&m_mem_root); 459 } 460 free_memory(myf root_alloc_flags)461 void free_memory(myf root_alloc_flags) 462 { 463 free_root(&m_mem_root, root_alloc_flags); 464 } 465 strmake(const char * str,size_t len)466 char* strmake(const char *str, size_t len) 467 { 468 return strmake_root(&m_mem_root, str, len); 469 } 470 invalidate_changed_tables_in_cache()471 void invalidate_changed_tables_in_cache() 472 { 473 if (m_changed_tables) 474 query_cache.invalidate(m_changed_tables); 475 } 476 477 bool add_changed_table(const char *key, long key_length); 478 ha_trx_info(enum_trx_scope scope)479 Ha_trx_info* ha_trx_info(enum_trx_scope scope) 480 { 481 return m_scope_info[scope].m_ha_list; 482 } 483 ha_trx_info(enum_trx_scope scope)484 const Ha_trx_info* ha_trx_info(enum_trx_scope scope) const 485 { 486 return m_scope_info[scope].m_ha_list; 487 } 488 set_ha_trx_info(enum_trx_scope scope,Ha_trx_info * trx_info)489 void set_ha_trx_info(enum_trx_scope scope, Ha_trx_info *trx_info) 490 { 491 m_scope_info[scope].m_ha_list= trx_info; 492 } 493 xid_state()494 XID_STATE *xid_state() 495 { 496 return &m_xid_state; 497 } 498 xid_state()499 const XID_STATE *xid_state() const 500 { 501 return &m_xid_state; 502 } 503 cannot_safely_rollback(enum_trx_scope scope)504 bool cannot_safely_rollback(enum_trx_scope scope) const 505 { 506 return m_scope_info[scope].cannot_safely_rollback(); 507 } 508 get_unsafe_rollback_flags(enum_trx_scope scope)509 unsigned int get_unsafe_rollback_flags(enum_trx_scope scope) const 510 { 511 return m_scope_info[scope].get_unsafe_rollback_flags(); 512 } 513 set_unsafe_rollback_flags(enum_trx_scope scope,unsigned int flags)514 void set_unsafe_rollback_flags(enum_trx_scope scope, unsigned int flags) 515 { 516 m_scope_info[scope].set_unsafe_rollback_flags(flags); 517 } 518 add_unsafe_rollback_flags(enum_trx_scope scope,unsigned int flags)519 void add_unsafe_rollback_flags(enum_trx_scope scope, unsigned int flags) 520 { 521 m_scope_info[scope].add_unsafe_rollback_flags(flags); 522 } 523 reset_unsafe_rollback_flags(enum_trx_scope scope)524 void reset_unsafe_rollback_flags(enum_trx_scope scope) 525 { 526 m_scope_info[scope].reset_unsafe_rollback_flags(); 527 } 528 mark_modified_non_trans_table(enum_trx_scope scope)529 void mark_modified_non_trans_table(enum_trx_scope scope) 530 { 531 m_scope_info[scope].mark_modified_non_trans_table(); 532 } 533 has_modified_non_trans_table(enum_trx_scope scope)534 bool has_modified_non_trans_table(enum_trx_scope scope) const 535 { 536 return m_scope_info[scope].has_modified_non_trans_table(); 537 } 538 mark_created_temp_table(enum_trx_scope scope)539 void mark_created_temp_table(enum_trx_scope scope) 540 { 541 m_scope_info[scope].mark_created_temp_table(); 542 } 543 has_created_temp_table(enum_trx_scope scope)544 bool has_created_temp_table(enum_trx_scope scope) const 545 { 546 return m_scope_info[scope].has_created_temp_table(); 547 } 548 mark_dropped_temp_table(enum_trx_scope scope)549 void mark_dropped_temp_table(enum_trx_scope scope) 550 { 551 m_scope_info[scope].mark_dropped_temp_table(); 552 } 553 has_dropped_temp_table(enum_trx_scope scope)554 bool has_dropped_temp_table(enum_trx_scope scope) const 555 { 556 return m_scope_info[scope].has_dropped_temp_table(); 557 } 558 reset(enum_trx_scope scope)559 void reset(enum_trx_scope scope) 560 { 561 m_scope_info[scope].reset(); 562 } 563 is_empty(enum_trx_scope scope)564 bool is_empty(enum_trx_scope scope) const 565 { 566 return m_scope_info[scope].is_empty(); 567 } 568 set_no_2pc(enum_trx_scope scope,bool value)569 void set_no_2pc(enum_trx_scope scope, bool value) 570 { 571 m_scope_info[scope].m_no_2pc= value; 572 } 573 no_2pc(enum_trx_scope scope)574 bool no_2pc(enum_trx_scope scope) const 575 { 576 return m_scope_info[scope].m_no_2pc; 577 } 578 rw_ha_count(enum_trx_scope scope)579 int rw_ha_count(enum_trx_scope scope) const 580 { 581 return m_scope_info[scope].m_rw_ha_count; 582 } 583 set_rw_ha_count(enum_trx_scope scope,int value)584 void set_rw_ha_count(enum_trx_scope scope, int value) 585 { 586 m_scope_info[scope].m_rw_ha_count= value; 587 } 588 reset_scope(enum_trx_scope scope)589 void reset_scope(enum_trx_scope scope) 590 { 591 m_scope_info[scope].m_ha_list= 0; 592 m_scope_info[scope].m_no_2pc=0; 593 m_scope_info[scope].m_rw_ha_count= 0; 594 } 595 596 private: 597 CHANGED_TABLE_LIST* changed_table_dup(const char *key, long key_length); 598 599 /* routings to adding tables to list of changed in transaction tables */ list_include(CHANGED_TABLE_LIST ** prev,CHANGED_TABLE_LIST * curr,CHANGED_TABLE_LIST * new_table)600 bool list_include(CHANGED_TABLE_LIST** prev, 601 CHANGED_TABLE_LIST* curr, 602 CHANGED_TABLE_LIST* new_table) 603 { 604 if (new_table) 605 { 606 *prev= new_table; 607 (*prev)->next= curr; 608 return false; 609 } 610 else 611 return true; 612 } 613 614 public: get_rpl_transaction_ctx()615 Rpl_transaction_ctx *get_rpl_transaction_ctx() 616 { 617 return &m_rpl_transaction_ctx; 618 } 619 get_rpl_transaction_ctx()620 const Rpl_transaction_ctx *get_rpl_transaction_ctx() const 621 { 622 return &m_rpl_transaction_ctx; 623 } 624 get_transaction_write_set_ctx()625 Rpl_transaction_write_set_ctx *get_transaction_write_set_ctx() 626 { 627 return &m_transaction_write_set_ctx; 628 } 629 get_transaction_write_set_ctx()630 const Rpl_transaction_write_set_ctx *get_transaction_write_set_ctx() const 631 { 632 return &m_transaction_write_set_ctx; 633 } 634 635 private: 636 Rpl_transaction_ctx m_rpl_transaction_ctx; 637 Rpl_transaction_write_set_ctx m_transaction_write_set_ctx; 638 }; 639 640 #endif 641