1 /* 2 Copyright (c) 2013, 2021, Oracle and/or its affiliates. 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 Foundation, 22 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */ 23 24 #ifndef XA_H_INCLUDED 25 #define XA_H_INCLUDED 26 27 #include "my_global.h" // ulonglong 28 #include "mysql/plugin.h" // MYSQL_XIDDATASIZE 29 #include "mysqld.h" // server_id 30 #include "sql_cmd.h" 31 #include "sql_plugin_ref.h" // plugin_ref 32 #include <string.h> 33 #include "xa_aux.h" 34 35 class Protocol; 36 class THD; 37 struct xid_t; 38 39 enum xa_option_words {XA_NONE, XA_JOIN, XA_RESUME, XA_ONE_PHASE, 40 XA_SUSPEND, XA_FOR_MIGRATE}; 41 42 static const int TC_HEURISTIC_NOT_USED= 0; 43 static const int TC_HEURISTIC_RECOVER_COMMIT= 1; 44 static const int TC_HEURISTIC_RECOVER_ROLLBACK= 2; 45 46 /** 47 This class represents SQL statement which starts an XA transaction 48 with the given xid value. 49 */ 50 51 class Sql_cmd_xa_start : public Sql_cmd 52 { 53 public: Sql_cmd_xa_start(xid_t * xid_arg,enum xa_option_words xa_option)54 Sql_cmd_xa_start(xid_t *xid_arg, enum xa_option_words xa_option) 55 : m_xid(xid_arg), m_xa_opt(xa_option) 56 {} 57 sql_command_code()58 virtual enum_sql_command sql_command_code() const 59 { 60 return SQLCOM_XA_START; 61 } 62 63 virtual bool execute(THD *thd); 64 65 private: 66 bool trans_xa_start(THD *thd); 67 xid_t *m_xid; 68 enum xa_option_words m_xa_opt; 69 }; 70 71 72 /** 73 This class represents SQL statement which puts in the IDLE state 74 an XA transaction with the given xid value. 75 */ 76 77 class Sql_cmd_xa_end : public Sql_cmd 78 { 79 public: Sql_cmd_xa_end(xid_t * xid_arg,enum xa_option_words xa_option)80 Sql_cmd_xa_end(xid_t *xid_arg, enum xa_option_words xa_option) 81 : m_xid(xid_arg), m_xa_opt(xa_option) 82 {} 83 sql_command_code()84 virtual enum_sql_command sql_command_code() const 85 { 86 return SQLCOM_XA_END; 87 } 88 89 virtual bool execute(THD *thd); 90 91 private: 92 bool trans_xa_end(THD *thd); 93 94 xid_t *m_xid; 95 enum xa_option_words m_xa_opt; 96 }; 97 98 99 /** 100 This class represents SQL statement which puts in the PREPARED state 101 an XA transaction with the given xid value. 102 */ 103 104 class Sql_cmd_xa_prepare : public Sql_cmd 105 { 106 public: Sql_cmd_xa_prepare(xid_t * xid_arg)107 explicit Sql_cmd_xa_prepare(xid_t *xid_arg) 108 : m_xid(xid_arg) 109 {} 110 sql_command_code()111 virtual enum_sql_command sql_command_code() const 112 { 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 /** 126 This class represents SQL statement which returns to a client 127 a list of XID's prepared to a XA commit/rollback. 128 */ 129 130 class Sql_cmd_xa_recover : public Sql_cmd 131 { 132 public: Sql_cmd_xa_recover(bool print_xid_as_hex)133 explicit Sql_cmd_xa_recover(bool print_xid_as_hex) 134 : m_print_xid_as_hex(print_xid_as_hex) 135 {} 136 sql_command_code()137 virtual enum_sql_command sql_command_code() const 138 { 139 return SQLCOM_XA_RECOVER; 140 } 141 142 virtual bool execute(THD *thd); 143 144 private: 145 bool trans_xa_recover(THD *thd); 146 147 bool m_print_xid_as_hex; 148 }; 149 150 151 /** 152 This class represents SQL statement which commits 153 and terminates an XA transaction with the given xid value. 154 */ 155 156 class Sql_cmd_xa_commit : public Sql_cmd 157 { 158 public: Sql_cmd_xa_commit(xid_t * xid_arg,enum xa_option_words xa_option)159 Sql_cmd_xa_commit(xid_t *xid_arg, enum xa_option_words xa_option) 160 : m_xid(xid_arg), m_xa_opt(xa_option) 161 {} 162 sql_command_code()163 virtual enum_sql_command sql_command_code() const 164 { 165 return SQLCOM_XA_COMMIT; 166 } 167 168 virtual bool execute(THD *thd); 169 get_xa_opt()170 enum xa_option_words get_xa_opt() const 171 { 172 return m_xa_opt; 173 } 174 private: 175 bool trans_xa_commit(THD *thd); 176 177 xid_t *m_xid; 178 enum xa_option_words m_xa_opt; 179 }; 180 181 182 /** 183 This class represents SQL statement which rollbacks and 184 terminates an XA transaction with the given xid value. 185 */ 186 187 class Sql_cmd_xa_rollback : public Sql_cmd 188 { 189 public: Sql_cmd_xa_rollback(xid_t * xid_arg)190 explicit Sql_cmd_xa_rollback(xid_t *xid_arg) 191 : m_xid(xid_arg) 192 {} 193 sql_command_code()194 virtual enum_sql_command sql_command_code() const 195 { 196 return SQLCOM_XA_ROLLBACK; 197 } 198 199 virtual bool execute(THD *thd); 200 201 private: 202 bool trans_xa_rollback(THD *thd); 203 204 xid_t *m_xid; 205 }; 206 207 208 typedef ulonglong my_xid; // this line is the same as in log_event.h 209 #define MYSQL_XID_PREFIX "MySQLXid" 210 #define XIDDATASIZE MYSQL_XIDDATASIZE 211 class XID_STATE; 212 213 /** 214 struct xid_t is binary compatible with the XID structure as 215 in the X/Open CAE Specification, Distributed Transaction Processing: 216 The XA Specification, X/Open Company Ltd., 1991. 217 http://www.opengroup.org/bookstore/catalog/c193.htm 218 219 @see MYSQL_XID in mysql/plugin.h 220 */ 221 typedef struct xid_t 222 { 223 private: 224 static const uint MYSQL_XID_PREFIX_LEN= 8; // must be a multiple of 8 225 static const uint MYSQL_XID_OFFSET= MYSQL_XID_PREFIX_LEN + sizeof(server_id); 226 static const uint MYSQL_XID_GTRID_LEN= MYSQL_XID_OFFSET + sizeof(my_xid); 227 228 /** 229 -1 means that the XID is null 230 */ 231 long formatID; 232 233 /** 234 value from 1 through 64 235 */ 236 long gtrid_length; 237 238 /** 239 value from 1 through 64 240 */ 241 long bqual_length; 242 243 /** 244 distributed trx identifier. not \0-terminated. 245 */ 246 char data[XIDDATASIZE]; 247 248 public: xid_txid_t249 xid_t() 250 : formatID(-1), 251 gtrid_length(0), 252 bqual_length(0) 253 { 254 memset(data, 0, XIDDATASIZE); 255 } 256 get_format_idxid_t257 long get_format_id() const 258 { 259 return formatID; 260 } 261 set_format_idxid_t262 void set_format_id(long v) 263 { 264 formatID= v; 265 } 266 get_gtrid_lengthxid_t267 long get_gtrid_length() const 268 { 269 return gtrid_length; 270 } 271 set_gtrid_lengthxid_t272 void set_gtrid_length(long v) 273 { 274 gtrid_length= v; 275 } 276 get_bqual_lengthxid_t277 long get_bqual_length() const 278 { 279 return bqual_length; 280 } 281 set_bqual_lengthxid_t282 void set_bqual_length(long v) 283 { 284 bqual_length= v; 285 } 286 get_dataxid_t287 const char* get_data() const 288 { 289 return data; 290 } 291 set_dataxid_t292 void set_data(const void* v, long l) 293 { 294 assert(l <= XIDDATASIZE); 295 memcpy(data, v, l); 296 } 297 resetxid_t298 void reset() 299 { 300 formatID= -1; 301 gtrid_length= 0; 302 bqual_length= 0; 303 memset(data, 0, XIDDATASIZE); 304 } 305 setxid_t306 void set(long f, const char *g, long gl, const char *b, long bl) 307 { 308 formatID= f; 309 memcpy(data, g, gtrid_length= gl); 310 memcpy(data + gl, b, bqual_length= bl); 311 } 312 get_my_xidxid_t313 my_xid get_my_xid() const 314 { 315 if (gtrid_length == static_cast<long>(MYSQL_XID_GTRID_LEN) && 316 bqual_length == 0 && 317 !memcmp(data, MYSQL_XID_PREFIX, MYSQL_XID_PREFIX_LEN)) 318 { 319 my_xid tmp; 320 memcpy(&tmp, data + MYSQL_XID_OFFSET, sizeof(tmp)); 321 return tmp; 322 } 323 return 0; 324 } 325 keyxid_t326 uchar *key() 327 { 328 return reinterpret_cast<uchar *>(>rid_length); 329 } 330 keyxid_t331 const uchar *key() const 332 { 333 return reinterpret_cast<const uchar*>(>rid_length); 334 } 335 key_lengthxid_t336 uint key_length() const 337 { 338 return sizeof(gtrid_length) + sizeof(bqual_length) + 339 gtrid_length + bqual_length; 340 } 341 342 /* 343 The size of the string containing serialized Xid representation 344 is computed as a sum of 345 eight as the number of formatting symbols (X'',X'',) 346 plus 2 x XIDDATASIZE (2 due to hex format), 347 plus space for decimal digits of XID::formatID, 348 plus one for 0x0. 349 */ 350 static const uint ser_buf_size= 351 8 + 2 * XIDDATASIZE + 4 * sizeof(long) + 1; 352 353 /** 354 The method fills XID in a buffer in format of GTRID,BQUAL,FORMATID 355 where GTRID, BQUAL are represented as hex strings. 356 357 @param buf a pointer to buffer 358 @return the value of the first argument 359 */ 360 serializexid_t361 char *serialize(char *buf) const 362 { 363 return serialize_xid(buf, formatID, gtrid_length, bqual_length, data); 364 } 365 366 #ifndef NDEBUG 367 /** 368 Get printable XID value. 369 370 @param buf pointer to the buffer where printable XID value has to be stored 371 372 @return pointer to the buffer passed in the first argument 373 */ 374 char* xid_to_str(char *buf) const; 375 #endif 376 eqxid_t377 bool eq(const xid_t *xid) const 378 { 379 return xid->formatID == formatID && 380 xid->gtrid_length == gtrid_length && 381 xid->bqual_length == bqual_length && 382 !memcmp(xid->data, data, gtrid_length + bqual_length); 383 } 384 is_nullxid_t385 bool is_null() const 386 { 387 return formatID == -1; 388 } 389 390 private: setxid_t391 void set(const xid_t *xid) 392 { 393 memcpy(this, xid, sizeof(xid->formatID) + xid->key_length()); 394 } 395 setxid_t396 void set(my_xid xid) 397 { 398 formatID= 1; 399 memcpy(data, MYSQL_XID_PREFIX, MYSQL_XID_PREFIX_LEN); 400 memcpy(data + MYSQL_XID_PREFIX_LEN, &server_id, sizeof(server_id)); 401 memcpy(data + MYSQL_XID_OFFSET, &xid, sizeof(xid)); 402 gtrid_length= MYSQL_XID_GTRID_LEN; 403 bqual_length= 0; 404 } 405 nullxid_t406 void null() 407 { 408 formatID= -1; 409 } 410 411 friend class XID_STATE; 412 } XID; 413 414 415 class XID_STATE 416 { 417 public: 418 enum xa_states {XA_NOTR=0, XA_ACTIVE, XA_IDLE, XA_PREPARED, XA_ROLLBACK_ONLY}; 419 420 /** 421 Transaction identifier. 422 For now, this is only used to catch duplicated external xids. 423 */ 424 private: 425 static const char *xa_state_names[]; 426 427 XID m_xid; 428 /// Used by external XA only 429 xa_states xa_state; 430 bool in_recovery; 431 /// Error reported by the Resource Manager (RM) to the Transaction Manager. 432 uint rm_error; 433 /* 434 XA-prepare binary logging status. The flag serves as a facility 435 to conduct XA transaction two round binary logging. 436 It is set to @c false at XA-start. 437 It is set to @c true by binlogging routine of XA-prepare handler as well 438 as recovered to @c true at the server recovery upon restart. 439 Checked and reset at XA-commit/rollback. 440 */ 441 bool m_is_binlogged; 442 443 public: XID_STATE()444 XID_STATE() 445 : xa_state(XA_NOTR), 446 in_recovery(false), 447 rm_error(0), 448 m_is_binlogged(false) 449 { m_xid.null(); } 450 set_state(xa_states state)451 void set_state(xa_states state) 452 { xa_state= state; } 453 get_state()454 enum xa_states get_state() 455 { return xa_state; } 456 has_state(xa_states state)457 bool has_state(xa_states state) const 458 { return xa_state == state; } 459 state_name()460 const char* state_name() const 461 { return xa_state_names[xa_state]; } 462 get_xid()463 const XID *get_xid() const 464 { return &m_xid; } 465 get_xid()466 XID *get_xid() 467 { return &m_xid; } 468 has_same_xid(const XID * xid)469 bool has_same_xid(const XID *xid) const 470 { return m_xid.eq(xid); } 471 set_query_id(query_id_t query_id)472 void set_query_id(query_id_t query_id) 473 { 474 if (m_xid.is_null()) 475 m_xid.set(query_id); 476 } 477 478 void set_error(THD *thd); 479 reset_error()480 void reset_error() 481 { rm_error= 0; } 482 cleanup()483 void cleanup() 484 { 485 /* 486 If rm_error is raised, it means that this piece of a distributed 487 transaction has failed and must be rolled back. But the user must 488 rollback it explicitly, so don't start a new distributed XA until 489 then. 490 */ 491 if (!rm_error) 492 m_xid.null(); 493 } 494 reset()495 void reset() 496 { 497 xa_state= XA_NOTR; 498 m_xid.null(); 499 in_recovery= false; 500 m_is_binlogged= false; 501 } 502 start_normal_xa(const XID * xid)503 void start_normal_xa(const XID *xid) 504 { 505 assert(m_xid.is_null()); 506 xa_state= XA_ACTIVE; 507 m_xid.set(xid); 508 in_recovery= false; 509 rm_error= 0; 510 } 511 512 void start_recovery_xa(const XID *xid, bool binlogged_arg= false) 513 { 514 xa_state= XA_PREPARED; 515 m_xid.set(xid); 516 in_recovery= true; 517 rm_error= 0; 518 m_is_binlogged= binlogged_arg; 519 } 520 is_in_recovery()521 bool is_in_recovery() const 522 { return in_recovery; } 523 is_binlogged()524 bool is_binlogged() const 525 { return m_is_binlogged; } 526 set_binlogged()527 void set_binlogged() 528 { m_is_binlogged= true; } 529 unset_binlogged()530 void unset_binlogged() 531 { m_is_binlogged= false; } 532 533 void store_xid_info(Protocol *protocol, bool print_xid_as_hex) const; 534 535 /** 536 Mark a XA transaction as rollback-only if the RM unilaterally 537 rolled back the transaction branch. 538 539 @note If a rollback was requested by the RM, this function sets 540 the appropriate rollback error code and transits the state 541 to XA_ROLLBACK_ONLY. 542 543 @return true if transaction was rolled back or if the transaction 544 state is XA_ROLLBACK_ONLY. false otherwise. 545 */ 546 547 bool xa_trans_rolled_back(); 548 549 550 /** 551 Check that XA transaction is in state IDLE or PREPARED. 552 553 @param report_error true if state IDLE or PREPARED has to be interpreted 554 as an error, else false 555 556 @return result of check 557 @retval false XA transaction is NOT in state IDLE or PREPARED 558 @retval true XA transaction is in state IDLE or PREPARED 559 */ 560 561 bool check_xa_idle_or_prepared(bool report_error) const; 562 563 564 /** 565 Check that XA transaction has an uncommitted work. Report an error 566 to a mysql user in case when there is an uncommitted work for XA transaction. 567 568 @return result of check 569 @retval false XA transaction is NOT in state IDLE, PREPARED 570 or ROLLBACK_ONLY. 571 @retval true XA transaction is in state IDLE or PREPARED 572 or ROLLBACK_ONLY. 573 */ 574 575 bool check_has_uncommitted_xa() const; 576 577 578 /** 579 Check if an XA transaction has been started. 580 581 @param report_error true if report an error in case when 582 XA transaction has been stared, else false. 583 584 @return result of check 585 @retval false XA transaction hasn't been started (XA_NOTR) 586 @retval true XA transaction has been started (!XA_NOTR) 587 */ 588 589 bool check_in_xa(bool report_error) const; 590 }; 591 592 593 class Transaction_ctx; 594 595 /** 596 Initialize a cache to store Transaction_ctx and a mutex to protect access 597 to the cache 598 599 @return result of initialization 600 @retval false success 601 @retval true failure 602 */ 603 604 bool transaction_cache_init(); 605 606 607 /** 608 Search information about XA transaction by a XID value. 609 610 @param xid Pointer to a XID structure that identifies a XA transaction. 611 612 @return pointer to a Transaction_ctx that describes the whole transaction 613 including XA-specific information (XID_STATE). 614 @retval NULL failure 615 @retval != NULL success 616 */ 617 618 Transaction_ctx *transaction_cache_search(XID *xid); 619 620 621 /** 622 Insert information about XA transaction into a cache indexed by XID. 623 624 @param xid Pointer to a XID structure that identifies a XA transaction. 625 @param transaction 626 Pointer to Transaction object that is inserted. 627 628 @return operation result 629 @retval false success or a cache already contains XID_STATE 630 for this XID value 631 @retval true failure 632 */ 633 634 bool transaction_cache_insert(XID *xid, Transaction_ctx *transaction); 635 636 /** 637 Transaction is marked in the cache as if it's recovered. 638 The method allows to sustain prepared transaction disconnection. 639 640 @param transaction 641 Pointer to Transaction object that is replaced. 642 643 @return operation result 644 @retval false success or a cache already contains XID_STATE 645 for this XID value 646 @retval true failure 647 */ 648 649 bool transaction_cache_detach(Transaction_ctx *transaction); 650 651 652 /** 653 Insert information about XA transaction being recovered into a cache 654 indexed by XID. 655 656 @param xid Pointer to a XID structure that identifies a XA transaction. 657 658 @return operation result 659 @retval false success or a cache already contains Transaction_ctx 660 for this XID value 661 @retval true failure 662 */ 663 664 bool transaction_cache_insert_recovery(XID *xid); 665 666 667 /** 668 Remove information about transaction from a cache. 669 670 @param transaction Pointer to a Transaction_ctx that has to be removed 671 from a cache. 672 */ 673 674 void transaction_cache_delete(Transaction_ctx *transaction); 675 676 677 /** 678 Release resources occupied by transaction cache. 679 */ 680 681 void transaction_cache_free(); 682 683 684 /** 685 This is a specific to "slave" applier collection of standard cleanup 686 actions to reset XA transaction state at the end of XA prepare rather than 687 to do it at the transaction commit, see @c ha_commit_one_phase. 688 THD of the slave applier is dissociated from a transaction object in engine 689 that continues to exist there. 690 691 @param THD current thread 692 @return the value of is_error() 693 */ 694 695 bool applier_reset_xa_trans(THD *thd); 696 697 698 /* interface to randomly access plugin data */ 699 struct st_plugin_int *plugin_find_by_type(const LEX_CSTRING &plugin, int type); 700 701 /** 702 The function detaches existing storage engines transaction 703 context from thd. Backup area to save it is provided to low level 704 storage engine function. 705 706 is invoked by plugin_foreach() after 707 trans_xa_start() for each storage engine. 708 709 @param[in,out] thd Thread context 710 @param plugin Reference to handlerton 711 712 @return FALSE on success, TRUE otherwise. 713 */ 714 715 my_bool detach_native_trx(THD *thd, plugin_ref plugin, 716 void *unused); 717 /** 718 The function reattaches existing storage engines transaction 719 context to thd. Backup area to save it is provided to low level 720 storage engine function. 721 722 is invoked by plugin_foreach() after 723 trans_xa_prepare() for each storage engine. 724 725 @param[in,out] thd Thread context 726 @param plugin Reference to handlerton 727 728 @return FALSE on success, 729 TRUE otherwise. 730 */ 731 732 my_bool reattach_native_trx(THD *thd, plugin_ref plugin, void *); 733 734 /** 735 Reset some transaction state information and delete corresponding 736 Transaction_ctx object from cache. 737 738 @param thd Current thread 739 */ 740 741 void cleanup_trans_state(THD *thd); 742 743 744 /** 745 Rollback the active XA transaction. 746 747 @note Resets rm_error before calling ha_rollback(), so 748 the thd->transaction.xid structure gets reset 749 by ha_rollback() / THD::transaction::cleanup(). 750 751 @return true if the rollback failed, false otherwise. 752 */ 753 754 bool xa_trans_force_rollback(THD *thd); 755 #endif 756