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 *>(&gtrid_length);
329   }
330 
keyxid_t331   const uchar *key() const
332   {
333     return reinterpret_cast<const uchar*>(&gtrid_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