1 /* 2 Copyright (c) 2013, 2019, Oracle and/or its affiliates. All rights reserved. 3 4 This program is free software; you can redistribute it and/or modify 5 it under the terms of the GNU General Public License, version 2.0, 6 as published by the Free Software Foundation. 7 8 This program is also distributed with certain software (including 9 but not limited to OpenSSL) that is licensed under separate terms, 10 as designated in a particular file or component or in included license 11 documentation. The authors of MySQL hereby grant you an additional 12 permission to link the program and your derivative works with the 13 separately licensed software that they have included with MySQL. 14 15 This program is distributed in the hope that it will be useful, 16 but WITHOUT ANY WARRANTY; without even the implied warranty of 17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 GNU General Public License, version 2.0, for more details. 19 20 You should have received a copy of the GNU General Public License 21 along with this program; if not, write to the Free Software 22 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ 23 24 #ifndef XA_H_INCLUDED 25 #define XA_H_INCLUDED 26 27 #include <string.h> 28 #include <sys/types.h> 29 #include <list> 30 #include <mutex> 31 32 #include "lex_string.h" 33 #include "my_dbug.h" 34 #include "my_inttypes.h" 35 #include "my_sqlcommand.h" 36 #include "sql/malloc_allocator.h" // Malloc_allocator 37 #include "sql/psi_memory_key.h" // key_memory_Recovered_xa_transactions 38 #include "sql/sql_cmd.h" // Sql_cmd 39 #include "sql/sql_list.h" // List 40 #include "sql/sql_plugin_ref.h" // plugin_ref 41 #include "sql/xa_aux.h" // serialize_xid 42 43 class Protocol; 44 class THD; 45 struct xid_t; 46 47 typedef int64 query_id_t; 48 49 enum xa_option_words { 50 XA_NONE, 51 XA_JOIN, 52 XA_RESUME, 53 XA_ONE_PHASE, 54 XA_SUSPEND, 55 XA_FOR_MIGRATE 56 }; 57 58 static const int TC_HEURISTIC_NOT_USED = 0; 59 static const int TC_HEURISTIC_RECOVER_COMMIT = 1; 60 static const int TC_HEURISTIC_RECOVER_ROLLBACK = 2; 61 62 /** 63 This class represents SQL statement which starts an XA transaction 64 with the given xid value. 65 */ 66 67 class Sql_cmd_xa_start : public Sql_cmd { 68 public: Sql_cmd_xa_start(xid_t * xid_arg,enum xa_option_words xa_option)69 Sql_cmd_xa_start(xid_t *xid_arg, enum xa_option_words xa_option) 70 : m_xid(xid_arg), m_xa_opt(xa_option) {} 71 sql_command_code()72 virtual enum_sql_command sql_command_code() const { return SQLCOM_XA_START; } 73 74 virtual bool execute(THD *thd); 75 76 private: 77 bool trans_xa_start(THD *thd); 78 xid_t *m_xid; 79 enum xa_option_words m_xa_opt; 80 }; 81 82 /** 83 This class represents SQL statement which puts in the IDLE state 84 an XA transaction with the given xid value. 85 */ 86 87 class Sql_cmd_xa_end : public Sql_cmd { 88 public: Sql_cmd_xa_end(xid_t * xid_arg,enum xa_option_words xa_option)89 Sql_cmd_xa_end(xid_t *xid_arg, enum xa_option_words xa_option) 90 : m_xid(xid_arg), m_xa_opt(xa_option) {} 91 sql_command_code()92 virtual enum_sql_command sql_command_code() const { return SQLCOM_XA_END; } 93 94 virtual bool execute(THD *thd); 95 96 private: 97 bool trans_xa_end(THD *thd); 98 99 xid_t *m_xid; 100 enum xa_option_words m_xa_opt; 101 }; 102 103 /** 104 This class represents SQL statement which puts in the PREPARED state 105 an XA transaction with the given xid value. 106 */ 107 108 class Sql_cmd_xa_prepare : public Sql_cmd { 109 public: Sql_cmd_xa_prepare(xid_t * xid_arg)110 explicit Sql_cmd_xa_prepare(xid_t *xid_arg) : m_xid(xid_arg) {} 111 sql_command_code()112 virtual enum_sql_command sql_command_code() const { 113 return SQLCOM_XA_PREPARE; 114 } 115 116 virtual bool execute(THD *thd); 117 118 private: 119 bool trans_xa_prepare(THD *thd); 120 121 xid_t *m_xid; 122 }; 123 124 /** 125 This class represents SQL statement which returns to a client 126 a list of XID's prepared to a XA commit/rollback. 127 */ 128 129 class Sql_cmd_xa_recover : public Sql_cmd { 130 public: Sql_cmd_xa_recover(bool print_xid_as_hex)131 explicit Sql_cmd_xa_recover(bool print_xid_as_hex) 132 : m_print_xid_as_hex(print_xid_as_hex) {} 133 sql_command_code()134 virtual enum_sql_command sql_command_code() const { 135 return SQLCOM_XA_RECOVER; 136 } 137 138 virtual bool execute(THD *thd); 139 140 private: 141 bool check_xa_recover_privilege(THD *thd) const; 142 bool trans_xa_recover(THD *thd); 143 144 bool m_print_xid_as_hex; 145 }; 146 147 class XID_STATE; 148 /** 149 This class represents SQL statement which commits 150 and terminates an XA transaction with the given xid value. 151 */ 152 153 class Sql_cmd_xa_commit : public Sql_cmd { 154 public: Sql_cmd_xa_commit(xid_t * xid_arg,enum xa_option_words xa_option)155 Sql_cmd_xa_commit(xid_t *xid_arg, enum xa_option_words xa_option) 156 : m_xid(xid_arg), m_xa_opt(xa_option) {} 157 sql_command_code()158 virtual enum_sql_command sql_command_code() const { return SQLCOM_XA_COMMIT; } 159 160 virtual bool execute(THD *thd); 161 get_xa_opt()162 enum xa_option_words get_xa_opt() const { return m_xa_opt; } 163 164 private: 165 bool trans_xa_commit(THD *thd); 166 bool process_external_xa_commit(THD *thd, xid_t *xid, XID_STATE *xid_state); 167 bool process_internal_xa_commit(THD *thd, XID_STATE *xid_state); 168 169 xid_t *m_xid; 170 enum xa_option_words m_xa_opt; 171 }; 172 173 /** 174 This class represents SQL statement which rollbacks and 175 terminates an XA transaction with the given xid value. 176 */ 177 178 class Sql_cmd_xa_rollback : public Sql_cmd { 179 public: Sql_cmd_xa_rollback(xid_t * xid_arg)180 explicit Sql_cmd_xa_rollback(xid_t *xid_arg) : m_xid(xid_arg) {} 181 sql_command_code()182 virtual enum_sql_command sql_command_code() const { 183 return SQLCOM_XA_ROLLBACK; 184 } 185 186 virtual bool execute(THD *thd); 187 188 private: 189 bool trans_xa_rollback(THD *thd); 190 bool process_external_xa_rollback(THD *thd, xid_t *xid, XID_STATE *xid_state); 191 bool process_internal_xa_rollback(THD *thd, XID_STATE *xid_state); 192 193 xid_t *m_xid; 194 }; 195 196 typedef ulonglong my_xid; // this line is the same as in log_event.h 197 #define MYSQL_XID_PREFIX "MySQLXid" 198 199 /* 200 Same as MYSQL_XIDDATASIZE but we do not want to include plugin.h here 201 See static_assert in .cc file. 202 */ 203 #define XIDDATASIZE 128 204 205 /** 206 struct xid_t is binary compatible with the XID structure as 207 in the X/Open CAE Specification, Distributed Transaction Processing: 208 The XA Specification, X/Open Company Ltd., 1991. 209 http://www.opengroup.org/bookstore/catalog/c193.htm 210 211 @see MYSQL_XID in mysql/plugin.h 212 */ 213 typedef struct xid_t { 214 private: 215 /** 216 -1 means that the XID is null 217 */ 218 long formatID; 219 220 /** 221 value from 1 through 64 222 */ 223 long gtrid_length; 224 225 /** 226 value from 1 through 64 227 */ 228 long bqual_length; 229 230 /** 231 distributed trx identifier. not \0-terminated. 232 */ 233 char data[XIDDATASIZE]; 234 235 public: xid_txid_t236 xid_t() : formatID(-1), gtrid_length(0), bqual_length(0) { 237 memset(data, 0, XIDDATASIZE); 238 } 239 get_format_idxid_t240 long get_format_id() const { return formatID; } 241 set_format_idxid_t242 void set_format_id(long v) { 243 DBUG_TRACE; 244 DBUG_PRINT("debug", ("SETTING XID_STATE formatID: %ld", v)); 245 formatID = v; 246 return; 247 } 248 get_gtrid_lengthxid_t249 long get_gtrid_length() const { return gtrid_length; } 250 set_gtrid_lengthxid_t251 void set_gtrid_length(long v) { gtrid_length = v; } 252 get_bqual_lengthxid_t253 long get_bqual_length() const { return bqual_length; } 254 set_bqual_lengthxid_t255 void set_bqual_length(long v) { bqual_length = v; } 256 get_dataxid_t257 const char *get_data() const { return data; } 258 set_dataxid_t259 void set_data(const void *v, long l) { 260 DBUG_ASSERT(l <= XIDDATASIZE); 261 memcpy(data, v, l); 262 } 263 resetxid_t264 void reset() { 265 formatID = -1; 266 gtrid_length = 0; 267 bqual_length = 0; 268 memset(data, 0, XIDDATASIZE); 269 } 270 setxid_t271 void set(long f, const char *g, long gl, const char *b, long bl) { 272 DBUG_TRACE; 273 DBUG_PRINT("debug", ("SETTING XID_STATE formatID: %ld", f)); 274 formatID = f; 275 memcpy(data, g, gtrid_length = gl); 276 bqual_length = bl; 277 if (bl > 0) memcpy(data + gl, b, bl); 278 return; 279 } 280 281 my_xid get_my_xid() const; 282 keyxid_t283 uchar *key() { return reinterpret_cast<uchar *>(>rid_length); } 284 keyxid_t285 const uchar *key() const { 286 return reinterpret_cast<const uchar *>(>rid_length); 287 } 288 key_lengthxid_t289 uint key_length() const { 290 return sizeof(gtrid_length) + sizeof(bqual_length) + gtrid_length + 291 bqual_length; 292 } 293 294 /* 295 The size of the string containing serialized Xid representation 296 is computed as a sum of 297 eight as the number of formatting symbols (X'',X'',) 298 plus 2 x XIDDATASIZE (2 due to hex format), 299 plus space for decimal digits of XID::formatID, 300 plus one for 0x0. 301 */ 302 static const uint ser_buf_size = 8 + 2 * XIDDATASIZE + 4 * sizeof(long) + 1; 303 304 /** 305 The method fills XID in a buffer in format of GTRID,BQUAL,FORMATID 306 where GTRID, BQUAL are represented as hex strings. 307 308 @param buf a pointer to buffer 309 @return the value of the first argument 310 */ 311 serializexid_t312 char *serialize(char *buf) const { 313 return serialize_xid(buf, formatID, gtrid_length, bqual_length, data); 314 } 315 316 #ifndef DBUG_OFF 317 /** 318 Get printable XID value. 319 320 @param buf pointer to the buffer where printable XID value has to be 321 stored 322 323 @return pointer to the buffer passed in the first argument 324 */ 325 char *xid_to_str(char *buf) const; 326 #endif 327 eqxid_t328 bool eq(const xid_t *xid) const { 329 return xid->formatID == formatID && xid->gtrid_length == gtrid_length && 330 xid->bqual_length == bqual_length && 331 !memcmp(xid->data, data, gtrid_length + bqual_length); 332 } 333 is_nullxid_t334 bool is_null() const { return formatID == -1; } 335 336 private: setxid_t337 void set(const xid_t *xid) { 338 memcpy(this, xid, sizeof(xid->formatID) + xid->key_length()); 339 } 340 341 void set(my_xid xid); 342 nullxid_t343 void null() { formatID = -1; } 344 345 friend class XID_STATE; 346 } XID; 347 348 struct st_handler_tablename; 349 350 /** 351 Plain structure to store information about XA transaction id 352 and a list of table names involved into XA transaction with 353 specified id. 354 */ 355 typedef struct st_xarecover_txn { 356 XID id; 357 List<st_handler_tablename> *mod_tables; 358 } XA_recover_txn; 359 360 class XID_STATE { 361 public: 362 enum xa_states { 363 XA_NOTR = 0, 364 XA_ACTIVE, 365 XA_IDLE, 366 XA_PREPARED, 367 XA_ROLLBACK_ONLY 368 }; 369 370 /** 371 Transaction identifier. 372 For now, this is only used to catch duplicated external xids. 373 */ 374 private: 375 static const char *xa_state_names[]; 376 377 XID m_xid; 378 /** 379 This mutex used for eliminating a possibility to run two 380 XA COMMIT/XA ROLLBACK statements concurrently against the same xid value. 381 m_xa_lock is used on handling XA COMMIT/XA ROLLBACK and acquired only for 382 external XA branches. 383 */ 384 std::mutex m_xa_lock; 385 386 /// Used by external XA only 387 xa_states xa_state; 388 bool in_recovery; 389 /// Error reported by the Resource Manager (RM) to the Transaction Manager. 390 uint rm_error; 391 /* 392 XA-prepare binary logging status. The flag serves as a facility 393 to conduct XA transaction two round binary logging. 394 It is set to @c false at XA-start. 395 It is set to @c true by binlogging routine of XA-prepare handler as well 396 as recovered to @c true at the server recovery upon restart. 397 Checked and reset at XA-commit/rollback. 398 */ 399 bool m_is_binlogged; 400 401 public: XID_STATE()402 XID_STATE() 403 : xa_state(XA_NOTR), 404 in_recovery(false), 405 rm_error(0), 406 m_is_binlogged(false) { 407 m_xid.null(); 408 } 409 get_xa_lock()410 std::mutex &get_xa_lock() { return m_xa_lock; } 411 set_state(xa_states state)412 void set_state(xa_states state) { xa_state = state; } 413 get_state()414 enum xa_states get_state() { return xa_state; } 415 has_state(xa_states state)416 bool has_state(xa_states state) const { return xa_state == state; } 417 state_name()418 const char *state_name() const { return xa_state_names[xa_state]; } 419 get_xid()420 const XID *get_xid() const { return &m_xid; } 421 get_xid()422 XID *get_xid() { return &m_xid; } 423 has_same_xid(const XID * xid)424 bool has_same_xid(const XID *xid) const { return m_xid.eq(xid); } 425 set_query_id(query_id_t query_id)426 void set_query_id(query_id_t query_id) { 427 if (m_xid.is_null()) m_xid.set(query_id); 428 } 429 430 void set_error(THD *thd); 431 reset_error()432 void reset_error() { rm_error = 0; } 433 cleanup()434 void cleanup() { 435 /* 436 If rm_error is raised, it means that this piece of a distributed 437 transaction has failed and must be rolled back. But the user must 438 rollback it explicitly, so don't start a new distributed XA until 439 then. 440 */ 441 if (!rm_error) m_xid.null(); 442 } 443 reset()444 void reset() { 445 xa_state = XA_NOTR; 446 m_xid.null(); 447 in_recovery = false; 448 m_is_binlogged = false; 449 } 450 start_normal_xa(const XID * xid)451 void start_normal_xa(const XID *xid) { 452 DBUG_ASSERT(m_xid.is_null()); 453 xa_state = XA_ACTIVE; 454 m_xid.set(xid); 455 in_recovery = false; 456 rm_error = 0; 457 } 458 459 void start_recovery_xa(const XID *xid, bool binlogged_arg = false) { 460 xa_state = XA_PREPARED; 461 m_xid.set(xid); 462 in_recovery = true; 463 rm_error = 0; 464 m_is_binlogged = binlogged_arg; 465 } 466 is_in_recovery()467 bool is_in_recovery() const { return in_recovery; } 468 is_binlogged()469 bool is_binlogged() const { return m_is_binlogged; } 470 set_binlogged()471 void set_binlogged() { m_is_binlogged = true; } 472 unset_binlogged()473 void unset_binlogged() { m_is_binlogged = false; } 474 475 void store_xid_info(Protocol *protocol, bool print_xid_as_hex) const; 476 477 /** 478 Mark a XA transaction as rollback-only if the RM unilaterally 479 rolled back the transaction branch. 480 481 @note If a rollback was requested by the RM, this function sets 482 the appropriate rollback error code and transits the state 483 to XA_ROLLBACK_ONLY. 484 485 @return true if transaction was rolled back or if the transaction 486 state is XA_ROLLBACK_ONLY. false otherwise. 487 */ 488 489 bool xa_trans_rolled_back(); 490 491 /** 492 Check that XA transaction is in state IDLE or PREPARED. 493 494 @param report_error true if state IDLE or PREPARED has to be interpreted 495 as an error, else false 496 497 @return result of check 498 @retval false XA transaction is NOT in state IDLE or PREPARED 499 @retval true XA transaction is in state IDLE or PREPARED 500 */ 501 502 bool check_xa_idle_or_prepared(bool report_error) const; 503 504 /** 505 Check that XA transaction has an uncommitted work. Report an error 506 to a mysql user in case when there is an uncommitted work for XA 507 transaction. 508 509 @return result of check 510 @retval false XA transaction is NOT in state IDLE, PREPARED 511 or ROLLBACK_ONLY. 512 @retval true XA transaction is in state IDLE or PREPARED 513 or ROLLBACK_ONLY. 514 */ 515 516 bool check_has_uncommitted_xa() const; 517 518 /** 519 Check if an XA transaction has been started. 520 521 @param report_error true if report an error in case when 522 XA transaction has been stared, else false. 523 524 @return result of check 525 @retval false XA transaction hasn't been started (XA_NOTR) 526 @retval true XA transaction has been started (!XA_NOTR) 527 */ 528 529 bool check_in_xa(bool report_error) const; 530 }; 531 532 /** 533 This class servers as a registry for prepared XA transactions existed before 534 server was shutdown and being resurrected during the server restart. 535 The class is singleton. To collect a list of XA transaction identifiers and 536 a list of tables for that MDL locks have be acquired the method 537 add_prepared_xa_transaction() must be called. This method is invoked by 538 the function trx_recover_for_mysql() called by innobase_xa_recover during 539 running of X/Open XA distributed transaction recovery procedure. After a list 540 of XA transaction identifiers and a list of table names to be locked in MDL 541 have been collected and the function ha_recover() has returned control flow 542 the method recover_prepared_xa_transactions() must be called to resurrect 543 prepared XA transactions. Separation of collecting information about prepared 544 XA transactions from restoring XA transactions is done in order to exclude 545 possible suspending on MDL locks inside the function 546 dd::reset_tables_and_tablespaces() that is called right after the function 547 ha_recover() returns control flow. 548 */ 549 550 class Recovered_xa_transactions { 551 public: 552 /** 553 Initialize singleton. 554 */ 555 static bool init(); 556 557 /** 558 Cleanup and delete singleton object 559 */ 560 static void destroy(); 561 562 /** 563 Get instance of the class Recovered_xa_transactions 564 */ 565 static Recovered_xa_transactions &instance(); 566 567 /** 568 Add information about prepared XA transaction into a list of 569 XA transactions to resurrect. 570 571 @param prepared_xa_trn information about prepared XA transaction 572 573 @return false on success, else true 574 */ 575 bool add_prepared_xa_transaction(XA_recover_txn *prepared_xa_trn); 576 577 /** 578 Iterate along a list of prepared XA transactions, register every XA 579 transaction in a cache and acquire MDL locks for every table taking part in 580 XA transaction being resurrected. 581 582 @return false on success, else true 583 */ 584 bool recover_prepared_xa_transactions(); 585 586 /** 587 Get initialized MEM_ROOT. 588 589 @return Pointer to a initialized MEM_ROOT. 590 */ 591 MEM_ROOT *get_allocated_memroot(); 592 593 private: 594 // Disable direct instantiation. Use the method init() instead. 595 Recovered_xa_transactions(); 596 597 static Recovered_xa_transactions *m_instance; 598 std::list<XA_recover_txn *, Malloc_allocator<XA_recover_txn *>> 599 m_prepared_xa_trans; 600 bool m_mem_root_inited; 601 MEM_ROOT m_mem_root; 602 }; 603 604 class Transaction_ctx; 605 606 /** 607 Initialize a cache to store Transaction_ctx and a mutex to protect access 608 to the cache 609 610 @return result of initialization 611 @retval false success 612 @retval true failure 613 */ 614 615 bool transaction_cache_init(); 616 617 /** 618 Transaction is marked in the cache as if it's recovered. 619 The method allows to sustain prepared transaction disconnection. 620 621 @param transaction 622 Pointer to Transaction object that is replaced. 623 624 @return operation result 625 @retval false success or a cache already contains XID_STATE 626 for this XID value 627 @retval true failure 628 */ 629 630 bool transaction_cache_detach(Transaction_ctx *transaction); 631 632 /** 633 Remove information about transaction from a cache. 634 635 @param transaction Pointer to a Transaction_ctx that has to be removed 636 from a cache. 637 */ 638 639 void transaction_cache_delete(Transaction_ctx *transaction); 640 641 /** 642 Release resources occupied by transaction cache. 643 */ 644 645 void transaction_cache_free(); 646 647 /** 648 This is a specific to "slave" applier collection of standard cleanup 649 actions to reset XA transaction state at the end of XA prepare rather than 650 to do it at the transaction commit, see @c ha_commit_one_phase. 651 THD of the slave applier is dissociated from a transaction object in engine 652 that continues to exist there. 653 654 @param thd current thread 655 @return the value of is_error() 656 */ 657 658 bool applier_reset_xa_trans(THD *thd); 659 660 /* interface to randomly access plugin data */ 661 struct st_plugin_int *plugin_find_by_type(const LEX_CSTRING &plugin, int type); 662 663 /** 664 The function detaches existing storage engines transaction 665 context from thd. Backup area to save it is provided to low level 666 storage engine function. 667 668 is invoked by plugin_foreach() after 669 trans_xa_start() for each storage engine. 670 671 @param[in,out] thd Thread context 672 @param plugin Reference to handlerton 673 674 @return false on success, true otherwise. 675 */ 676 677 bool detach_native_trx(THD *thd, plugin_ref plugin, void *); 678 679 /** 680 The function reattaches existing storage engines transaction 681 context to thd. Backup area to save it is provided to low level 682 storage engine function. 683 684 is invoked by plugin_foreach() after 685 trans_xa_prepare() for each storage engine. 686 687 @param[in,out] thd Thread context 688 @param plugin Reference to handlerton 689 690 @return false on success, 691 true otherwise. 692 */ 693 694 bool reattach_native_trx(THD *thd, plugin_ref plugin, void *); 695 696 /** 697 Reset some transaction state information and delete corresponding 698 Transaction_ctx object from cache. 699 700 @param thd Current thread 701 */ 702 703 void cleanup_trans_state(THD *thd); 704 705 /** 706 Rollback the active XA transaction. 707 708 @note Resets rm_error before calling ha_rollback(), so 709 the thd->transaction.xid structure gets reset 710 by ha_rollback() / THD::transaction::cleanup(). 711 712 @return true if the rollback failed, false otherwise. 713 */ 714 715 bool xa_trans_force_rollback(THD *thd); 716 #endif 717