MySQL 5.6.14 Source Code Document
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
transaction.cc
1 /* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
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 as published by
5  the Free Software Foundation; version 2 of the License.
6 
7  This program is distributed in the hope that it will be useful,
8  but WITHOUT ANY WARRANTY; without even the implied warranty of
9  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  GNU General Public License for more details.
11 
12  You should have received a copy of the GNU General Public License
13  along with this program; if not, write to the Free Software Foundation,
14  51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */
15 
16 
17 #include "sql_priv.h"
18 #include "transaction.h"
19 #include "rpl_handler.h"
20 #include "debug_sync.h" // DEBUG_SYNC
21 #include "sql_acl.h" // SUPER_ACL
22 
33 bool trans_check_state(THD *thd)
34 {
35  enum xa_states xa_state= thd->transaction.xid_state.xa_state;
36  DBUG_ENTER("trans_check");
37 
38  /*
39  Always commit statement transaction before manipulating with
40  the normal one.
41  */
42  DBUG_ASSERT(thd->transaction.stmt.is_empty());
43 
44  if (unlikely(thd->in_sub_stmt))
45  my_error(ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG, MYF(0));
46  if (xa_state != XA_NOTR)
47  my_error(ER_XAER_RMFAIL, MYF(0), xa_state_names[xa_state]);
48  else
49  DBUG_RETURN(FALSE);
50 
51  DBUG_RETURN(TRUE);
52 }
53 
54 
66 static bool xa_trans_rolled_back(XID_STATE *xid_state)
67 {
68  if (xid_state->rm_error)
69  {
70  switch (xid_state->rm_error) {
71  case ER_LOCK_WAIT_TIMEOUT:
72  my_error(ER_XA_RBTIMEOUT, MYF(0));
73  break;
74  case ER_LOCK_DEADLOCK:
75  my_error(ER_XA_RBDEADLOCK, MYF(0));
76  break;
77  default:
78  my_error(ER_XA_RBROLLBACK, MYF(0));
79  }
80  xid_state->xa_state= XA_ROLLBACK_ONLY;
81  }
82 
83  return (xid_state->xa_state == XA_ROLLBACK_ONLY);
84 }
85 
86 
97 static bool xa_trans_force_rollback(THD *thd)
98 {
99  /*
100  We must reset rm_error before calling ha_rollback(),
101  so thd->transaction.xid structure gets reset
102  by ha_rollback()/THD::transaction::cleanup().
103  */
104  thd->transaction.xid_state.rm_error= 0;
105  if (ha_rollback_trans(thd, true))
106  {
107  my_error(ER_XAER_RMERR, MYF(0));
108  return true;
109  }
110  return false;
111 }
112 
113 
127 bool trans_begin(THD *thd, uint flags)
128 {
129  int res= FALSE;
130  DBUG_ENTER("trans_begin");
131 
132  if (trans_check_state(thd))
133  DBUG_RETURN(TRUE);
134 
135  thd->locked_tables_list.unlock_locked_tables(thd);
136 
137  DBUG_ASSERT(!thd->locked_tables_mode);
138 
139  if (thd->in_multi_stmt_transaction_mode() ||
140  (thd->variables.option_bits & OPTION_TABLE_LOCK))
141  {
142  thd->variables.option_bits&= ~OPTION_TABLE_LOCK;
143  thd->server_status&=
144  ~(SERVER_STATUS_IN_TRANS | SERVER_STATUS_IN_TRANS_READONLY);
145  DBUG_PRINT("info", ("clearing SERVER_STATUS_IN_TRANS"));
146  res= test(ha_commit_trans(thd, TRUE));
147  }
148 
149  thd->variables.option_bits&= ~OPTION_BEGIN;
150  thd->transaction.all.reset_unsafe_rollback_flags();
151 
152  if (res)
153  DBUG_RETURN(TRUE);
154 
155  /*
156  Release transactional metadata locks only after the
157  transaction has been committed.
158  */
159  thd->mdl_context.release_transactional_locks();
160 
161  // The RO/RW options are mutually exclusive.
162  DBUG_ASSERT(!((flags & MYSQL_START_TRANS_OPT_READ_ONLY) &&
163  (flags & MYSQL_START_TRANS_OPT_READ_WRITE)));
164  if (flags & MYSQL_START_TRANS_OPT_READ_ONLY)
165  thd->tx_read_only= true;
166  else if (flags & MYSQL_START_TRANS_OPT_READ_WRITE)
167  {
168  /*
169  Explicitly starting a RW transaction when the server is in
170  read-only mode, is not allowed unless the user has SUPER priv.
171  Implicitly starting a RW transaction is allowed for backward
172  compatibility.
173  */
174  const bool user_is_super=
175  test(thd->security_ctx->master_access & SUPER_ACL);
176  if (opt_readonly && !user_is_super)
177  {
178  my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--read-only");
179  DBUG_RETURN(true);
180  }
181  thd->tx_read_only= false;
182  }
183 
184  thd->variables.option_bits|= OPTION_BEGIN;
185  thd->server_status|= SERVER_STATUS_IN_TRANS;
186  if (thd->tx_read_only)
187  thd->server_status|= SERVER_STATUS_IN_TRANS_READONLY;
188  DBUG_PRINT("info", ("setting SERVER_STATUS_IN_TRANS"));
189 
190  /* ha_start_consistent_snapshot() relies on OPTION_BEGIN flag set. */
191  if (flags & MYSQL_START_TRANS_OPT_WITH_CONS_SNAPSHOT)
192  res= ha_start_consistent_snapshot(thd);
193 
194  DBUG_RETURN(test(res));
195 }
196 
197 
207 bool trans_commit(THD *thd)
208 {
209  int res;
210  DBUG_ENTER("trans_commit");
211 
212 #ifndef DBUG_OFF
213  char buf1[256], buf2[256];
214  DBUG_PRINT("enter", ("stmt.ha_list: %s, all.ha_list: %s",
215  ha_list_names(thd->transaction.stmt.ha_list, buf1),
216  ha_list_names(thd->transaction.all.ha_list, buf2)));
217 
218  thd->transaction.stmt.dbug_unsafe_rollback_flags("stmt");
219  thd->transaction.all.dbug_unsafe_rollback_flags("all");
220 #endif
221 
222  if (trans_check_state(thd))
223  DBUG_RETURN(TRUE);
224 
225  thd->server_status&=
226  ~(SERVER_STATUS_IN_TRANS | SERVER_STATUS_IN_TRANS_READONLY);
227  DBUG_PRINT("info", ("clearing SERVER_STATUS_IN_TRANS"));
228  res= ha_commit_trans(thd, TRUE);
229  thd->variables.option_bits&= ~OPTION_BEGIN;
230  thd->transaction.all.reset_unsafe_rollback_flags();
231  thd->lex->start_transaction_opt= 0;
232 
233  DBUG_RETURN(test(res));
234 }
235 
236 
248 bool trans_commit_implicit(THD *thd)
249 {
250  bool res= FALSE;
251  DBUG_ENTER("trans_commit_implicit");
252 
253 #ifndef DBUG_OFF
254  char buf1[256], buf2[256];
255  DBUG_PRINT("enter", ("stmt.ha_list: %s, all.ha_list: %s",
256  ha_list_names(thd->transaction.stmt.ha_list, buf1),
257  ha_list_names(thd->transaction.all.ha_list, buf2)));
258 
259  thd->transaction.stmt.dbug_unsafe_rollback_flags("stmt");
260  thd->transaction.all.dbug_unsafe_rollback_flags("all");
261 #endif
262 
263  /*
264  Ensure that trans_check_state() was called before trans_commit_implicit()
265  by asserting that conditions that are checked in the former function are
266  true.
267  */
268  DBUG_ASSERT(thd->transaction.stmt.is_empty() &&
269  !thd->in_sub_stmt &&
270  thd->transaction.xid_state.xa_state == XA_NOTR);
271 
272  if (thd->in_multi_stmt_transaction_mode() ||
273  (thd->variables.option_bits & OPTION_TABLE_LOCK))
274  {
275  /* Safety if one did "drop table" on locked tables */
276  if (!thd->locked_tables_mode)
277  thd->variables.option_bits&= ~OPTION_TABLE_LOCK;
278  thd->server_status&=
279  ~(SERVER_STATUS_IN_TRANS | SERVER_STATUS_IN_TRANS_READONLY);
280  DBUG_PRINT("info", ("clearing SERVER_STATUS_IN_TRANS"));
281  res= test(ha_commit_trans(thd, TRUE));
282  }
283  else if (tc_log)
284  tc_log->commit(thd, true);
285 
286  thd->variables.option_bits&= ~OPTION_BEGIN;
287  thd->transaction.all.reset_unsafe_rollback_flags();
288 
289  /*
290  Upon implicit commit, reset the current transaction
291  isolation level and access mode. We do not care about
292  @@session.completion_type since it's documented
293  to not have any effect on implicit commit.
294  */
295  thd->tx_isolation= (enum_tx_isolation) thd->variables.tx_isolation;
296  thd->tx_read_only= thd->variables.tx_read_only;
297 
298  DBUG_RETURN(res);
299 }
300 
301 
311 bool trans_rollback(THD *thd)
312 {
313  int res;
314  DBUG_ENTER("trans_rollback");
315 
316 #ifndef DBUG_OFF
317  char buf1[256], buf2[256];
318  DBUG_PRINT("enter", ("stmt.ha_list: %s, all.ha_list: %s",
319  ha_list_names(thd->transaction.stmt.ha_list, buf1),
320  ha_list_names(thd->transaction.all.ha_list, buf2)));
321 
322  thd->transaction.stmt.dbug_unsafe_rollback_flags("stmt");
323  thd->transaction.all.dbug_unsafe_rollback_flags("all");
324 #endif
325 
326  if (trans_check_state(thd))
327  DBUG_RETURN(TRUE);
328 
329  thd->server_status&=
330  ~(SERVER_STATUS_IN_TRANS | SERVER_STATUS_IN_TRANS_READONLY);
331  DBUG_PRINT("info", ("clearing SERVER_STATUS_IN_TRANS"));
332  res= ha_rollback_trans(thd, TRUE);
333  thd->variables.option_bits&= ~OPTION_BEGIN;
334  thd->transaction.all.reset_unsafe_rollback_flags();
335  thd->lex->start_transaction_opt= 0;
336 
337  DBUG_RETURN(test(res));
338 }
339 
340 
356 bool trans_rollback_implicit(THD *thd)
357 {
358  int res;
359  DBUG_ENTER("trans_rollback_implict");
360 
361  /*
362  Always commit/rollback statement transaction before manipulating
363  with the normal one.
364  Don't perform rollback in the middle of sub-statement, wait till
365  its end.
366  */
367  DBUG_ASSERT(thd->transaction.stmt.is_empty() && !thd->in_sub_stmt);
368 
369  thd->server_status&=
370  ~(SERVER_STATUS_IN_TRANS | SERVER_STATUS_IN_TRANS_READONLY);
371  DBUG_PRINT("info", ("clearing SERVER_STATUS_IN_TRANS"));
372  res= ha_rollback_trans(thd, true);
373  /*
374  We don't reset OPTION_BEGIN flag below to simulate implicit start
375  of new transacton in @@autocommit=1 mode. This is necessary to
376  preserve backward compatibility.
377  */
378  thd->transaction.all.reset_unsafe_rollback_flags();
379 
380  /* Rollback should clear transaction_rollback_request flag. */
381  DBUG_ASSERT(! thd->transaction_rollback_request);
382 
383  DBUG_RETURN(test(res));
384 }
385 
386 
402 bool trans_commit_stmt(THD *thd)
403 {
404  DBUG_ENTER("trans_commit_stmt");
405 #ifndef DBUG_OFF
406  char buf1[256], buf2[256];
407  DBUG_PRINT("enter", ("stmt.ha_list: %s, all.ha_list: %s",
408  ha_list_names(thd->transaction.stmt.ha_list, buf1),
409  ha_list_names(thd->transaction.all.ha_list, buf2)));
410 #endif
411 
412  int res= FALSE;
413  /*
414  We currently don't invoke commit/rollback at end of
415  a sub-statement. In future, we perhaps should take
416  a savepoint for each nested statement, and release the
417  savepoint when statement has succeeded.
418  */
419  DBUG_ASSERT(! thd->in_sub_stmt);
420 
421 #ifndef DBUG_OFF
422  DBUG_PRINT("enter", ("stmt.ha_list: %s, all.ha_list: %s",
423  ha_list_names(thd->transaction.stmt.ha_list, buf1),
424  ha_list_names(thd->transaction.all.ha_list, buf2)));
425 
426  thd->transaction.stmt.dbug_unsafe_rollback_flags("stmt");
427  thd->transaction.all.dbug_unsafe_rollback_flags("all");
428 #endif
429 
430  thd->transaction.merge_unsafe_rollback_flags();
431 
432  if (thd->transaction.stmt.ha_list)
433  {
434  res= ha_commit_trans(thd, FALSE);
435  if (! thd->in_active_multi_stmt_transaction())
436  {
437  thd->tx_isolation= (enum_tx_isolation) thd->variables.tx_isolation;
438  thd->tx_read_only= thd->variables.tx_read_only;
439  }
440  }
441  else if (tc_log)
442  tc_log->commit(thd, false);
443 
444  thd->transaction.stmt.reset();
445 
446  DBUG_RETURN(test(res));
447 }
448 
449 
458 bool trans_rollback_stmt(THD *thd)
459 {
460  DBUG_ENTER("trans_rollback_stmt");
461 
462  /*
463  We currently don't invoke commit/rollback at end of
464  a sub-statement. In future, we perhaps should take
465  a savepoint for each nested statement, and release the
466  savepoint when statement has succeeded.
467  */
468  DBUG_ASSERT(! thd->in_sub_stmt);
469 
470 #ifndef DBUG_OFF
471  char buf1[256], buf2[256];
472  DBUG_PRINT("enter", ("stmt.ha_list: %s, all.ha_list: %s",
473  ha_list_names(thd->transaction.stmt.ha_list, buf1),
474  ha_list_names(thd->transaction.all.ha_list, buf2)));
475 
476  thd->transaction.stmt.dbug_unsafe_rollback_flags("stmt");
477  thd->transaction.all.dbug_unsafe_rollback_flags("all");
478 #endif
479 
480  thd->transaction.merge_unsafe_rollback_flags();
481 
482  if (thd->transaction.stmt.ha_list)
483  {
484  ha_rollback_trans(thd, FALSE);
485  if (! thd->in_active_multi_stmt_transaction())
486  {
487  thd->tx_isolation= (enum_tx_isolation) thd->variables.tx_isolation;
488  thd->tx_read_only= thd->variables.tx_read_only;
489  }
490  }
491  else if (tc_log)
492  tc_log->rollback(thd, false);
493 
494  thd->transaction.stmt.reset();
495 
496  DBUG_RETURN(FALSE);
497 }
498 
499 /* Find a named savepoint in the current transaction. */
500 static SAVEPOINT **
501 find_savepoint(THD *thd, LEX_STRING name)
502 {
503  SAVEPOINT **sv= &thd->transaction.savepoints;
504 
505  while (*sv)
506  {
507  if (my_strnncoll(system_charset_info, (uchar *) name.str, name.length,
508  (uchar *) (*sv)->name, (*sv)->length) == 0)
509  break;
510  sv= &(*sv)->prev;
511  }
512 
513  return sv;
514 }
515 
516 
527 bool trans_savepoint(THD *thd, LEX_STRING name)
528 {
529  SAVEPOINT **sv, *newsv;
530  DBUG_ENTER("trans_savepoint");
531 
532  if (!(thd->in_multi_stmt_transaction_mode() || thd->in_sub_stmt) ||
533  !opt_using_transactions)
534  DBUG_RETURN(FALSE);
535 
536  enum xa_states xa_state= thd->transaction.xid_state.xa_state;
537  if (xa_state != XA_NOTR && xa_state != XA_ACTIVE)
538  {
539  my_error(ER_XAER_RMFAIL, MYF(0), xa_state_names[xa_state]);
540  DBUG_RETURN(TRUE);
541  }
542 
543  sv= find_savepoint(thd, name);
544 
545  if (*sv) /* old savepoint of the same name exists */
546  {
547  newsv= *sv;
548  ha_release_savepoint(thd, *sv);
549  *sv= (*sv)->prev;
550  }
551  else if ((newsv= (SAVEPOINT *) alloc_root(&thd->transaction.mem_root,
552  savepoint_alloc_size)) == NULL)
553  {
554  my_error(ER_OUT_OF_RESOURCES, MYF(0));
555  DBUG_RETURN(TRUE);
556  }
557 
558  newsv->name= strmake_root(&thd->transaction.mem_root, name.str, name.length);
559  newsv->length= name.length;
560 
561  /*
562  if we'll get an error here, don't add new savepoint to the list.
563  we'll lose a little bit of memory in transaction mem_root, but it'll
564  be free'd when transaction ends anyway
565  */
566  if (ha_savepoint(thd, newsv))
567  DBUG_RETURN(TRUE);
568 
569  newsv->prev= thd->transaction.savepoints;
570  thd->transaction.savepoints= newsv;
571 
572  /*
573  Remember locks acquired before the savepoint was set.
574  They are used as a marker to only release locks acquired after
575  the setting of this savepoint.
576  Note: this works just fine if we're under LOCK TABLES,
577  since mdl_savepoint() is guaranteed to be beyond
578  the last locked table. This allows to release some
579  locks acquired during LOCK TABLES.
580  */
581  newsv->mdl_savepoint= thd->mdl_context.mdl_savepoint();
582 
583  DBUG_RETURN(FALSE);
584 }
585 
586 
604 bool trans_rollback_to_savepoint(THD *thd, LEX_STRING name)
605 {
606  int res= FALSE;
607  SAVEPOINT *sv= *find_savepoint(thd, name);
608  DBUG_ENTER("trans_rollback_to_savepoint");
609 
610 #ifndef DBUG_OFF
611  char buf1[256], buf2[256];
612  DBUG_PRINT("enter", ("stmt.ha_list: %s, all.ha_list: %s",
613  ha_list_names(thd->transaction.stmt.ha_list, buf1),
614  ha_list_names(thd->transaction.all.ha_list, buf2)));
615 
616  thd->transaction.stmt.dbug_unsafe_rollback_flags("stmt");
617  thd->transaction.all.dbug_unsafe_rollback_flags("all");
618 #endif
619 
620  if (sv == NULL)
621  {
622  my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "SAVEPOINT", name.str);
623  DBUG_RETURN(TRUE);
624  }
625 
626  enum xa_states xa_state= thd->transaction.xid_state.xa_state;
627  if (xa_state != XA_NOTR && xa_state != XA_ACTIVE)
628  {
629  my_error(ER_XAER_RMFAIL, MYF(0), xa_state_names[xa_state]);
630  DBUG_RETURN(TRUE);
631  }
632 
633  if (ha_rollback_to_savepoint(thd, sv))
634  res= TRUE;
635  else if (thd->transaction.all.cannot_safely_rollback() && !thd->slave_thread)
636  thd->transaction.push_unsafe_rollback_warnings(thd);
637 
638  thd->transaction.savepoints= sv;
639 
640  /*
641  Release metadata locks that were acquired during this savepoint unit
642  unless binlogging is on. Releasing locks with binlogging on can break
643  replication as it allows other connections to drop these tables before
644  rollback to savepoint is written to the binlog.
645  */
646  bool binlog_on= mysql_bin_log.is_open() && thd->variables.sql_log_bin;
647  if (!res && !binlog_on)
648  thd->mdl_context.rollback_to_savepoint(sv->mdl_savepoint);
649 
650  DBUG_RETURN(test(res));
651 }
652 
653 
668 bool trans_release_savepoint(THD *thd, LEX_STRING name)
669 {
670  int res= FALSE;
671  SAVEPOINT *sv= *find_savepoint(thd, name);
672  DBUG_ENTER("trans_release_savepoint");
673 
674  if (sv == NULL)
675  {
676  my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "SAVEPOINT", name.str);
677  DBUG_RETURN(TRUE);
678  }
679 
680  enum xa_states xa_state= thd->transaction.xid_state.xa_state;
681  if (xa_state != XA_NOTR && xa_state != XA_ACTIVE)
682  {
683  my_error(ER_XAER_RMFAIL, MYF(0), xa_state_names[xa_state]);
684  DBUG_RETURN(TRUE);
685  }
686 
687  if (ha_release_savepoint(thd, sv))
688  res= TRUE;
689 
690  thd->transaction.savepoints= sv->prev;
691 
692  DBUG_RETURN(test(res));
693 }
694 
695 
705 bool trans_xa_start(THD *thd)
706 {
707  enum xa_states xa_state= thd->transaction.xid_state.xa_state;
708  DBUG_ENTER("trans_xa_start");
709 
710  if (xa_state == XA_IDLE && thd->lex->xa_opt == XA_RESUME)
711  {
712  bool not_equal= !thd->transaction.xid_state.xid.eq(thd->lex->xid);
713  if (not_equal)
714  my_error(ER_XAER_NOTA, MYF(0));
715  else
716  thd->transaction.xid_state.xa_state= XA_ACTIVE;
717  DBUG_RETURN(not_equal);
718  }
719 
720  /* TODO: JOIN is not supported yet. */
721  if (thd->lex->xa_opt != XA_NONE)
722  my_error(ER_XAER_INVAL, MYF(0));
723  else if (xa_state != XA_NOTR)
724  my_error(ER_XAER_RMFAIL, MYF(0), xa_state_names[xa_state]);
725  else if (thd->locked_tables_mode || thd->in_active_multi_stmt_transaction())
726  my_error(ER_XAER_OUTSIDE, MYF(0));
727  else if (!trans_begin(thd))
728  {
729  DBUG_ASSERT(thd->transaction.xid_state.xid.is_null());
730  thd->transaction.xid_state.xa_state= XA_ACTIVE;
731  thd->transaction.xid_state.rm_error= 0;
732  thd->transaction.xid_state.xid.set(thd->lex->xid);
733  if (xid_cache_insert(&thd->transaction.xid_state))
734  {
735  thd->transaction.xid_state.xa_state= XA_NOTR;
736  thd->transaction.xid_state.xid.null();
737  trans_rollback(thd);
738  DBUG_RETURN(true);
739  }
740  DBUG_RETURN(FALSE);
741  }
742 
743  DBUG_RETURN(TRUE);
744 }
745 
746 
756 bool trans_xa_end(THD *thd)
757 {
758  DBUG_ENTER("trans_xa_end");
759 
760  /* TODO: SUSPEND and FOR MIGRATE are not supported yet. */
761  if (thd->lex->xa_opt != XA_NONE)
762  my_error(ER_XAER_INVAL, MYF(0));
763  else if (thd->transaction.xid_state.xa_state != XA_ACTIVE)
764  my_error(ER_XAER_RMFAIL, MYF(0),
765  xa_state_names[thd->transaction.xid_state.xa_state]);
766  else if (!thd->transaction.xid_state.xid.eq(thd->lex->xid))
767  my_error(ER_XAER_NOTA, MYF(0));
768  else if (!xa_trans_rolled_back(&thd->transaction.xid_state))
769  thd->transaction.xid_state.xa_state= XA_IDLE;
770 
771  DBUG_RETURN(thd->is_error() ||
772  thd->transaction.xid_state.xa_state != XA_IDLE);
773 }
774 
775 
785 bool trans_xa_prepare(THD *thd)
786 {
787  DBUG_ENTER("trans_xa_prepare");
788 
789  if (thd->transaction.xid_state.xa_state != XA_IDLE)
790  my_error(ER_XAER_RMFAIL, MYF(0),
791  xa_state_names[thd->transaction.xid_state.xa_state]);
792  else if (!thd->transaction.xid_state.xid.eq(thd->lex->xid))
793  my_error(ER_XAER_NOTA, MYF(0));
794  else if (ha_prepare(thd))
795  {
796  xid_cache_delete(&thd->transaction.xid_state);
797  thd->transaction.xid_state.xa_state= XA_NOTR;
798  my_error(ER_XA_RBROLLBACK, MYF(0));
799  }
800  else
801  thd->transaction.xid_state.xa_state= XA_PREPARED;
802 
803  DBUG_RETURN(thd->is_error() ||
804  thd->transaction.xid_state.xa_state != XA_PREPARED);
805 }
806 
807 
817 bool trans_xa_commit(THD *thd)
818 {
819  bool res= TRUE;
820  enum xa_states xa_state= thd->transaction.xid_state.xa_state;
821  DBUG_ENTER("trans_xa_commit");
822 
823  if (!thd->transaction.xid_state.xid.eq(thd->lex->xid))
824  {
825  /*
826  xid_state.in_thd is always true beside of xa recovery procedure.
827  Note, that there is no race condition here between xid_cache_search
828  and xid_cache_delete, since we always delete our own XID
829  (thd->lex->xid == thd->transaction.xid_state.xid).
830  The only case when thd->lex->xid != thd->transaction.xid_state.xid
831  and xid_state->in_thd == 0 is in the function
832  xa_cache_insert(XID, xa_states), which is called before starting
833  client connections, and thus is always single-threaded.
834  */
835  XID_STATE *xs= xid_cache_search(thd->lex->xid);
836  res= !xs || xs->in_thd;
837  if (res)
838  my_error(ER_XAER_NOTA, MYF(0));
839  else
840  {
841  res= xa_trans_rolled_back(xs);
842  ha_commit_or_rollback_by_xid(thd, thd->lex->xid, !res);
843  xid_cache_delete(xs);
844  }
845  DBUG_RETURN(res);
846  }
847 
848  if (xa_trans_rolled_back(&thd->transaction.xid_state))
849  {
850  xa_trans_force_rollback(thd);
851  res= thd->is_error();
852  }
853  else if (xa_state == XA_IDLE && thd->lex->xa_opt == XA_ONE_PHASE)
854  {
855  int r= ha_commit_trans(thd, TRUE);
856  if ((res= test(r)))
857  my_error(r == 1 ? ER_XA_RBROLLBACK : ER_XAER_RMERR, MYF(0));
858  }
859  else if (xa_state == XA_PREPARED && thd->lex->xa_opt == XA_NONE)
860  {
861  MDL_request mdl_request;
862 
863  /*
864  Acquire metadata lock which will ensure that COMMIT is blocked
865  by active FLUSH TABLES WITH READ LOCK (and vice versa COMMIT in
866  progress blocks FTWRL).
867 
868  We allow FLUSHer to COMMIT; we assume FLUSHer knows what it does.
869  */
870  mdl_request.init(MDL_key::COMMIT, "", "", MDL_INTENTION_EXCLUSIVE,
871  MDL_TRANSACTION);
872 
873  if (thd->mdl_context.acquire_lock(&mdl_request,
874  thd->variables.lock_wait_timeout))
875  {
876  ha_rollback_trans(thd, TRUE);
877  my_error(ER_XAER_RMERR, MYF(0));
878  }
879  else
880  {
881  DEBUG_SYNC(thd, "trans_xa_commit_after_acquire_commit_lock");
882 
883  if (tc_log)
884  res= test(tc_log->commit(thd, /* all */ true));
885  else
886  res= test(ha_commit_low(thd, /* all */ true));
887 
888  if (res)
889  my_error(ER_XAER_RMERR, MYF(0));
890  }
891  }
892  else
893  {
894  my_error(ER_XAER_RMFAIL, MYF(0), xa_state_names[xa_state]);
895  DBUG_RETURN(TRUE);
896  }
897 
898  thd->variables.option_bits&= ~OPTION_BEGIN;
899  thd->transaction.all.reset_unsafe_rollback_flags();
900  thd->server_status&=
901  ~(SERVER_STATUS_IN_TRANS | SERVER_STATUS_IN_TRANS_READONLY);
902  DBUG_PRINT("info", ("clearing SERVER_STATUS_IN_TRANS"));
903  xid_cache_delete(&thd->transaction.xid_state);
904  thd->transaction.xid_state.xa_state= XA_NOTR;
905 
906  DBUG_RETURN(res);
907 }
908 
909 
919 bool trans_xa_rollback(THD *thd)
920 {
921  bool res= TRUE;
922  enum xa_states xa_state= thd->transaction.xid_state.xa_state;
923  DBUG_ENTER("trans_xa_rollback");
924 
925  if (!thd->transaction.xid_state.xid.eq(thd->lex->xid))
926  {
927  XID_STATE *xs= xid_cache_search(thd->lex->xid);
928  if (!xs || xs->in_thd)
929  my_error(ER_XAER_NOTA, MYF(0));
930  else
931  {
932  xa_trans_rolled_back(xs);
933  ha_commit_or_rollback_by_xid(thd, thd->lex->xid, 0);
934  xid_cache_delete(xs);
935  }
936  DBUG_RETURN(thd->get_stmt_da()->is_error());
937  }
938 
939  if (xa_state != XA_IDLE && xa_state != XA_PREPARED && xa_state != XA_ROLLBACK_ONLY)
940  {
941  my_error(ER_XAER_RMFAIL, MYF(0), xa_state_names[xa_state]);
942  DBUG_RETURN(TRUE);
943  }
944 
945  res= xa_trans_force_rollback(thd);
946 
947  thd->variables.option_bits&= ~OPTION_BEGIN;
948  thd->transaction.all.reset_unsafe_rollback_flags();
949  thd->server_status&=
950  ~(SERVER_STATUS_IN_TRANS | SERVER_STATUS_IN_TRANS_READONLY);
951  DBUG_PRINT("info", ("clearing SERVER_STATUS_IN_TRANS"));
952  xid_cache_delete(&thd->transaction.xid_state);
953  thd->transaction.xid_state.xa_state= XA_NOTR;
954 
955  DBUG_RETURN(res);
956 }