~ubuntu-branches/ubuntu/precise/mysql-5.5/precise-201203300109

« back to all changes in this revision

Viewing changes to sql/transaction.cc

  • Committer: Package Import Robot
  • Author(s): Clint Byrum
  • Date: 2011-11-08 11:31:13 UTC
  • Revision ID: package-import@ubuntu.com-20111108113113-3ulw01fvi4vn8m25
Tags: upstream-5.5.17
ImportĀ upstreamĀ versionĀ 5.5.17

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* Copyright (c) 2000, 2011, 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
 
14
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
 
15
 
 
16
 
 
17
#ifdef USE_PRAGMA_IMPLEMENTATION
 
18
#pragma implementation                         // gcc: Class implementation
 
19
#endif
 
20
 
 
21
#include "sql_priv.h"
 
22
#include "transaction.h"
 
23
#include "rpl_handler.h"
 
24
#include "debug_sync.h"         // DEBUG_SYNC
 
25
 
 
26
/* Conditions under which the transaction state must not change. */
 
27
static bool trans_check(THD *thd)
 
28
{
 
29
  enum xa_states xa_state= thd->transaction.xid_state.xa_state;
 
30
  DBUG_ENTER("trans_check");
 
31
 
 
32
  /*
 
33
    Always commit statement transaction before manipulating with
 
34
    the normal one.
 
35
  */
 
36
  DBUG_ASSERT(thd->transaction.stmt.is_empty());
 
37
 
 
38
  if (unlikely(thd->in_sub_stmt))
 
39
    my_error(ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG, MYF(0));
 
40
  if (xa_state != XA_NOTR)
 
41
    my_error(ER_XAER_RMFAIL, MYF(0), xa_state_names[xa_state]);
 
42
  else
 
43
    DBUG_RETURN(FALSE);
 
44
 
 
45
  DBUG_RETURN(TRUE);
 
46
}
 
47
 
 
48
 
 
49
/**
 
50
  Mark a XA transaction as rollback-only if the RM unilaterally
 
51
  rolled back the transaction branch.
 
52
 
 
53
  @note If a rollback was requested by the RM, this function sets
 
54
        the appropriate rollback error code and transits the state
 
55
        to XA_ROLLBACK_ONLY.
 
56
 
 
57
  @return TRUE if transaction was rolled back or if the transaction
 
58
          state is XA_ROLLBACK_ONLY. FALSE otherwise.
 
59
*/
 
60
static bool xa_trans_rolled_back(XID_STATE *xid_state)
 
61
{
 
62
  if (xid_state->rm_error)
 
63
  {
 
64
    switch (xid_state->rm_error) {
 
65
    case ER_LOCK_WAIT_TIMEOUT:
 
66
      my_error(ER_XA_RBTIMEOUT, MYF(0));
 
67
      break;
 
68
    case ER_LOCK_DEADLOCK:
 
69
      my_error(ER_XA_RBDEADLOCK, MYF(0));
 
70
      break;
 
71
    default:
 
72
      my_error(ER_XA_RBROLLBACK, MYF(0));
 
73
    }
 
74
    xid_state->xa_state= XA_ROLLBACK_ONLY;
 
75
  }
 
76
 
 
77
  return (xid_state->xa_state == XA_ROLLBACK_ONLY);
 
78
}
 
79
 
 
80
 
 
81
/**
 
82
  Rollback the active XA transaction.
 
83
 
 
84
  @note Resets rm_error before calling ha_rollback(), so
 
85
        the thd->transaction.xid structure gets reset
 
86
        by ha_rollback() / THD::transaction::cleanup().
 
87
 
 
88
  @return TRUE if the rollback failed, FALSE otherwise.
 
89
*/
 
90
 
 
91
static bool xa_trans_force_rollback(THD *thd)
 
92
{
 
93
  /*
 
94
    We must reset rm_error before calling ha_rollback(),
 
95
    so thd->transaction.xid structure gets reset
 
96
    by ha_rollback()/THD::transaction::cleanup().
 
97
  */
 
98
  thd->transaction.xid_state.rm_error= 0;
 
99
  if (ha_rollback_trans(thd, true))
 
100
  {
 
101
    my_error(ER_XAER_RMERR, MYF(0));
 
102
    return true;
 
103
  }
 
104
  return false;
 
105
}
 
106
 
 
107
 
 
108
/**
 
109
  Begin a new transaction.
 
110
 
 
111
  @note Beginning a transaction implicitly commits any current
 
112
        transaction and releases existing locks.
 
113
 
 
114
  @param thd     Current thread
 
115
  @param flags   Transaction flags
 
116
 
 
117
  @retval FALSE  Success
 
118
  @retval TRUE   Failure
 
119
*/
 
120
 
 
121
bool trans_begin(THD *thd, uint flags)
 
122
{
 
123
  int res= FALSE;
 
124
  DBUG_ENTER("trans_begin");
 
125
 
 
126
  if (trans_check(thd))
 
127
    DBUG_RETURN(TRUE);
 
128
 
 
129
  thd->locked_tables_list.unlock_locked_tables(thd);
 
130
 
 
131
  DBUG_ASSERT(!thd->locked_tables_mode);
 
132
 
 
133
  if (thd->in_multi_stmt_transaction_mode() ||
 
134
      (thd->variables.option_bits & OPTION_TABLE_LOCK))
 
135
  {
 
136
    thd->variables.option_bits&= ~OPTION_TABLE_LOCK;
 
137
    thd->server_status&= ~SERVER_STATUS_IN_TRANS;
 
138
    res= test(ha_commit_trans(thd, TRUE));
 
139
  }
 
140
 
 
141
  thd->variables.option_bits&= ~(OPTION_BEGIN | OPTION_KEEP_LOG);
 
142
  thd->transaction.all.modified_non_trans_table= FALSE;
 
143
 
 
144
  if (res)
 
145
    DBUG_RETURN(TRUE);
 
146
 
 
147
  /*
 
148
    Release transactional metadata locks only after the
 
149
    transaction has been committed.
 
150
  */
 
151
  thd->mdl_context.release_transactional_locks();
 
152
 
 
153
  thd->variables.option_bits|= OPTION_BEGIN;
 
154
  thd->server_status|= SERVER_STATUS_IN_TRANS;
 
155
 
 
156
  if (flags & MYSQL_START_TRANS_OPT_WITH_CONS_SNAPSHOT)
 
157
    res= ha_start_consistent_snapshot(thd);
 
158
 
 
159
  DBUG_RETURN(test(res));
 
160
}
 
161
 
 
162
 
 
163
/**
 
164
  Commit the current transaction, making its changes permanent.
 
165
 
 
166
  @param thd     Current thread
 
167
 
 
168
  @retval FALSE  Success
 
169
  @retval TRUE   Failure
 
170
*/
 
171
 
 
172
bool trans_commit(THD *thd)
 
173
{
 
174
  int res;
 
175
  DBUG_ENTER("trans_commit");
 
176
 
 
177
  if (trans_check(thd))
 
178
    DBUG_RETURN(TRUE);
 
179
 
 
180
  thd->server_status&= ~SERVER_STATUS_IN_TRANS;
 
181
  res= ha_commit_trans(thd, TRUE);
 
182
  if (res)
 
183
    /*
 
184
      if res is non-zero, then ha_commit_trans has rolled back the
 
185
      transaction, so the hooks for rollback will be called.
 
186
    */
 
187
    RUN_HOOK(transaction, after_rollback, (thd, FALSE));
 
188
  else
 
189
    RUN_HOOK(transaction, after_commit, (thd, FALSE));
 
190
  thd->variables.option_bits&= ~(OPTION_BEGIN | OPTION_KEEP_LOG);
 
191
  thd->transaction.all.modified_non_trans_table= FALSE;
 
192
  thd->lex->start_transaction_opt= 0;
 
193
 
 
194
  DBUG_RETURN(test(res));
 
195
}
 
196
 
 
197
 
 
198
/**
 
199
  Implicitly commit the current transaction.
 
200
 
 
201
  @note A implicit commit does not releases existing table locks.
 
202
 
 
203
  @param thd     Current thread
 
204
 
 
205
  @retval FALSE  Success
 
206
  @retval TRUE   Failure
 
207
*/
 
208
 
 
209
bool trans_commit_implicit(THD *thd)
 
210
{
 
211
  bool res= FALSE;
 
212
  DBUG_ENTER("trans_commit_implicit");
 
213
 
 
214
  if (trans_check(thd))
 
215
    DBUG_RETURN(TRUE);
 
216
 
 
217
  if (thd->in_multi_stmt_transaction_mode() ||
 
218
      (thd->variables.option_bits & OPTION_TABLE_LOCK))
 
219
  {
 
220
    /* Safety if one did "drop table" on locked tables */
 
221
    if (!thd->locked_tables_mode)
 
222
      thd->variables.option_bits&= ~OPTION_TABLE_LOCK;
 
223
    thd->server_status&= ~SERVER_STATUS_IN_TRANS;
 
224
    res= test(ha_commit_trans(thd, TRUE));
 
225
  }
 
226
 
 
227
  thd->variables.option_bits&= ~(OPTION_BEGIN | OPTION_KEEP_LOG);
 
228
  thd->transaction.all.modified_non_trans_table= FALSE;
 
229
 
 
230
  /*
 
231
    Upon implicit commit, reset the current transaction
 
232
    isolation level. We do not care about
 
233
    @@session.completion_type since it's documented
 
234
    to not have any effect on implicit commit.
 
235
  */
 
236
  thd->tx_isolation= (enum_tx_isolation) thd->variables.tx_isolation;
 
237
 
 
238
  DBUG_RETURN(res);
 
239
}
 
240
 
 
241
 
 
242
/**
 
243
  Rollback the current transaction, canceling its changes.
 
244
 
 
245
  @param thd     Current thread
 
246
 
 
247
  @retval FALSE  Success
 
248
  @retval TRUE   Failure
 
249
*/
 
250
 
 
251
bool trans_rollback(THD *thd)
 
252
{
 
253
  int res;
 
254
  DBUG_ENTER("trans_rollback");
 
255
 
 
256
  if (trans_check(thd))
 
257
    DBUG_RETURN(TRUE);
 
258
 
 
259
  thd->server_status&= ~SERVER_STATUS_IN_TRANS;
 
260
  res= ha_rollback_trans(thd, TRUE);
 
261
  RUN_HOOK(transaction, after_rollback, (thd, FALSE));
 
262
  thd->variables.option_bits&= ~(OPTION_BEGIN | OPTION_KEEP_LOG);
 
263
  thd->transaction.all.modified_non_trans_table= FALSE;
 
264
  thd->lex->start_transaction_opt= 0;
 
265
 
 
266
  DBUG_RETURN(test(res));
 
267
}
 
268
 
 
269
 
 
270
/**
 
271
  Commit the single statement transaction.
 
272
 
 
273
  @note Note that if the autocommit is on, then the following call
 
274
        inside InnoDB will commit or rollback the whole transaction
 
275
        (= the statement). The autocommit mechanism built into InnoDB
 
276
        is based on counting locks, but if the user has used LOCK
 
277
        TABLES then that mechanism does not know to do the commit.
 
278
 
 
279
  @param thd     Current thread
 
280
 
 
281
  @retval FALSE  Success
 
282
  @retval TRUE   Failure
 
283
*/
 
284
 
 
285
bool trans_commit_stmt(THD *thd)
 
286
{
 
287
  DBUG_ENTER("trans_commit_stmt");
 
288
  int res= FALSE;
 
289
  /*
 
290
    We currently don't invoke commit/rollback at end of
 
291
    a sub-statement.  In future, we perhaps should take
 
292
    a savepoint for each nested statement, and release the
 
293
    savepoint when statement has succeeded.
 
294
  */
 
295
  DBUG_ASSERT(! thd->in_sub_stmt);
 
296
 
 
297
  if (thd->transaction.stmt.ha_list)
 
298
  {
 
299
    res= ha_commit_trans(thd, FALSE);
 
300
    if (! thd->in_active_multi_stmt_transaction())
 
301
      thd->tx_isolation= (enum_tx_isolation) thd->variables.tx_isolation;
 
302
  }
 
303
 
 
304
  if (res)
 
305
    /*
 
306
      if res is non-zero, then ha_commit_trans has rolled back the
 
307
      transaction, so the hooks for rollback will be called.
 
308
    */
 
309
    RUN_HOOK(transaction, after_rollback, (thd, FALSE));
 
310
  else
 
311
    RUN_HOOK(transaction, after_commit, (thd, FALSE));
 
312
 
 
313
  thd->transaction.stmt.reset();
 
314
 
 
315
  DBUG_RETURN(test(res));
 
316
}
 
317
 
 
318
 
 
319
/**
 
320
  Rollback the single statement transaction.
 
321
 
 
322
  @param thd     Current thread
 
323
 
 
324
  @retval FALSE  Success
 
325
  @retval TRUE   Failure
 
326
*/
 
327
bool trans_rollback_stmt(THD *thd)
 
328
{
 
329
  DBUG_ENTER("trans_rollback_stmt");
 
330
 
 
331
  /*
 
332
    We currently don't invoke commit/rollback at end of
 
333
    a sub-statement.  In future, we perhaps should take
 
334
    a savepoint for each nested statement, and release the
 
335
    savepoint when statement has succeeded.
 
336
  */
 
337
  DBUG_ASSERT(! thd->in_sub_stmt);
 
338
 
 
339
  if (thd->transaction.stmt.ha_list)
 
340
  {
 
341
    ha_rollback_trans(thd, FALSE);
 
342
    if (thd->transaction_rollback_request && !thd->in_sub_stmt)
 
343
      ha_rollback_trans(thd, TRUE);
 
344
    if (! thd->in_active_multi_stmt_transaction())
 
345
      thd->tx_isolation= (enum_tx_isolation) thd->variables.tx_isolation;
 
346
  }
 
347
 
 
348
  RUN_HOOK(transaction, after_rollback, (thd, FALSE));
 
349
 
 
350
  thd->transaction.stmt.reset();
 
351
 
 
352
  DBUG_RETURN(FALSE);
 
353
}
 
354
 
 
355
/* Find a named savepoint in the current transaction. */
 
356
static SAVEPOINT **
 
357
find_savepoint(THD *thd, LEX_STRING name)
 
358
{
 
359
  SAVEPOINT **sv= &thd->transaction.savepoints;
 
360
 
 
361
  while (*sv)
 
362
  {
 
363
    if (my_strnncoll(system_charset_info, (uchar *) name.str, name.length,
 
364
                     (uchar *) (*sv)->name, (*sv)->length) == 0)
 
365
      break;
 
366
    sv= &(*sv)->prev;
 
367
  }
 
368
 
 
369
  return sv;
 
370
}
 
371
 
 
372
 
 
373
/**
 
374
  Set a named transaction savepoint.
 
375
 
 
376
  @param thd    Current thread
 
377
  @param name   Savepoint name
 
378
 
 
379
  @retval FALSE  Success
 
380
  @retval TRUE   Failure
 
381
*/
 
382
 
 
383
bool trans_savepoint(THD *thd, LEX_STRING name)
 
384
{
 
385
  SAVEPOINT **sv, *newsv;
 
386
  DBUG_ENTER("trans_savepoint");
 
387
 
 
388
  if (!(thd->in_multi_stmt_transaction_mode() || thd->in_sub_stmt) ||
 
389
      !opt_using_transactions)
 
390
    DBUG_RETURN(FALSE);
 
391
 
 
392
  enum xa_states xa_state= thd->transaction.xid_state.xa_state;
 
393
  if (xa_state != XA_NOTR)
 
394
  {
 
395
    my_error(ER_XAER_RMFAIL, MYF(0), xa_state_names[xa_state]);
 
396
    DBUG_RETURN(TRUE);
 
397
  }
 
398
 
 
399
  sv= find_savepoint(thd, name);
 
400
 
 
401
  if (*sv) /* old savepoint of the same name exists */
 
402
  {
 
403
    newsv= *sv;
 
404
    ha_release_savepoint(thd, *sv);
 
405
    *sv= (*sv)->prev;
 
406
  }
 
407
  else if ((newsv= (SAVEPOINT *) alloc_root(&thd->transaction.mem_root,
 
408
                                            savepoint_alloc_size)) == NULL)
 
409
  {
 
410
    my_error(ER_OUT_OF_RESOURCES, MYF(0));
 
411
    DBUG_RETURN(TRUE);
 
412
  }
 
413
 
 
414
  newsv->name= strmake_root(&thd->transaction.mem_root, name.str, name.length);
 
415
  newsv->length= name.length;
 
416
 
 
417
  /*
 
418
    if we'll get an error here, don't add new savepoint to the list.
 
419
    we'll lose a little bit of memory in transaction mem_root, but it'll
 
420
    be free'd when transaction ends anyway
 
421
  */
 
422
  if (ha_savepoint(thd, newsv))
 
423
    DBUG_RETURN(TRUE);
 
424
 
 
425
  newsv->prev= thd->transaction.savepoints;
 
426
  thd->transaction.savepoints= newsv;
 
427
 
 
428
  /*
 
429
    Remember locks acquired before the savepoint was set.
 
430
    They are used as a marker to only release locks acquired after
 
431
    the setting of this savepoint.
 
432
    Note: this works just fine if we're under LOCK TABLES,
 
433
    since mdl_savepoint() is guaranteed to be beyond
 
434
    the last locked table. This allows to release some
 
435
    locks acquired during LOCK TABLES.
 
436
  */
 
437
  newsv->mdl_savepoint= thd->mdl_context.mdl_savepoint();
 
438
 
 
439
  DBUG_RETURN(FALSE);
 
440
}
 
441
 
 
442
 
 
443
/**
 
444
  Rollback a transaction to the named savepoint.
 
445
 
 
446
  @note Modifications that the current transaction made to
 
447
        rows after the savepoint was set are undone in the
 
448
        rollback.
 
449
 
 
450
  @note Savepoints that were set at a later time than the
 
451
        named savepoint are deleted.
 
452
 
 
453
  @param thd    Current thread
 
454
  @param name   Savepoint name
 
455
 
 
456
  @retval FALSE  Success
 
457
  @retval TRUE   Failure
 
458
*/
 
459
 
 
460
bool trans_rollback_to_savepoint(THD *thd, LEX_STRING name)
 
461
{
 
462
  int res= FALSE;
 
463
  SAVEPOINT *sv= *find_savepoint(thd, name);
 
464
  DBUG_ENTER("trans_rollback_to_savepoint");
 
465
 
 
466
  if (sv == NULL)
 
467
  {
 
468
    my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "SAVEPOINT", name.str);
 
469
    DBUG_RETURN(TRUE);
 
470
  }
 
471
 
 
472
  enum xa_states xa_state= thd->transaction.xid_state.xa_state;
 
473
  if (xa_state != XA_NOTR)
 
474
  {
 
475
    my_error(ER_XAER_RMFAIL, MYF(0), xa_state_names[xa_state]);
 
476
    DBUG_RETURN(TRUE);
 
477
  }
 
478
 
 
479
  if (ha_rollback_to_savepoint(thd, sv))
 
480
    res= TRUE;
 
481
  else if (((thd->variables.option_bits & OPTION_KEEP_LOG) ||
 
482
            thd->transaction.all.modified_non_trans_table) &&
 
483
           !thd->slave_thread)
 
484
    push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
 
485
                 ER_WARNING_NOT_COMPLETE_ROLLBACK,
 
486
                 ER(ER_WARNING_NOT_COMPLETE_ROLLBACK));
 
487
 
 
488
  thd->transaction.savepoints= sv;
 
489
 
 
490
  /*
 
491
    Release metadata locks that were acquired during this savepoint unit
 
492
    unless binlogging is on. Releasing locks with binlogging on can break
 
493
    replication as it allows other connections to drop these tables before
 
494
    rollback to savepoint is written to the binlog.
 
495
  */
 
496
  bool binlog_on= mysql_bin_log.is_open() && thd->variables.sql_log_bin;
 
497
  if (!res && !binlog_on)
 
498
    thd->mdl_context.rollback_to_savepoint(sv->mdl_savepoint);
 
499
 
 
500
  DBUG_RETURN(test(res));
 
501
}
 
502
 
 
503
 
 
504
/**
 
505
  Remove the named savepoint from the set of savepoints of
 
506
  the current transaction.
 
507
 
 
508
  @note No commit or rollback occurs. It is an error if the
 
509
        savepoint does not exist.
 
510
 
 
511
  @param thd    Current thread
 
512
  @param name   Savepoint name
 
513
 
 
514
  @retval FALSE  Success
 
515
  @retval TRUE   Failure
 
516
*/
 
517
 
 
518
bool trans_release_savepoint(THD *thd, LEX_STRING name)
 
519
{
 
520
  int res= FALSE;
 
521
  SAVEPOINT *sv= *find_savepoint(thd, name);
 
522
  DBUG_ENTER("trans_release_savepoint");
 
523
 
 
524
  if (sv == NULL)
 
525
  {
 
526
    my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "SAVEPOINT", name.str);
 
527
    DBUG_RETURN(TRUE);
 
528
  }
 
529
 
 
530
  if (ha_release_savepoint(thd, sv))
 
531
    res= TRUE;
 
532
 
 
533
  thd->transaction.savepoints= sv->prev;
 
534
 
 
535
  DBUG_RETURN(test(res));
 
536
}
 
537
 
 
538
 
 
539
/**
 
540
  Starts an XA transaction with the given xid value.
 
541
 
 
542
  @param thd    Current thread
 
543
 
 
544
  @retval FALSE  Success
 
545
  @retval TRUE   Failure
 
546
*/
 
547
 
 
548
bool trans_xa_start(THD *thd)
 
549
{
 
550
  enum xa_states xa_state= thd->transaction.xid_state.xa_state;
 
551
  DBUG_ENTER("trans_xa_start");
 
552
 
 
553
  if (xa_state == XA_IDLE && thd->lex->xa_opt == XA_RESUME)
 
554
  {
 
555
    bool not_equal= !thd->transaction.xid_state.xid.eq(thd->lex->xid);
 
556
    if (not_equal)
 
557
      my_error(ER_XAER_NOTA, MYF(0));
 
558
    else
 
559
      thd->transaction.xid_state.xa_state= XA_ACTIVE;
 
560
    DBUG_RETURN(not_equal);
 
561
  }
 
562
 
 
563
  /* TODO: JOIN is not supported yet. */
 
564
  if (thd->lex->xa_opt != XA_NONE)
 
565
    my_error(ER_XAER_INVAL, MYF(0));
 
566
  else if (xa_state != XA_NOTR)
 
567
    my_error(ER_XAER_RMFAIL, MYF(0), xa_state_names[xa_state]);
 
568
  else if (thd->locked_tables_mode || thd->in_active_multi_stmt_transaction())
 
569
    my_error(ER_XAER_OUTSIDE, MYF(0));
 
570
  else if (xid_cache_search(thd->lex->xid))
 
571
    my_error(ER_XAER_DUPID, MYF(0));
 
572
  else if (!trans_begin(thd))
 
573
  {
 
574
    DBUG_ASSERT(thd->transaction.xid_state.xid.is_null());
 
575
    thd->transaction.xid_state.xa_state= XA_ACTIVE;
 
576
    thd->transaction.xid_state.rm_error= 0;
 
577
    thd->transaction.xid_state.xid.set(thd->lex->xid);
 
578
    xid_cache_insert(&thd->transaction.xid_state);
 
579
    DBUG_RETURN(FALSE);
 
580
  }
 
581
 
 
582
  DBUG_RETURN(TRUE);
 
583
}
 
584
 
 
585
 
 
586
/**
 
587
  Put a XA transaction in the IDLE state.
 
588
 
 
589
  @param thd    Current thread
 
590
 
 
591
  @retval FALSE  Success
 
592
  @retval TRUE   Failure
 
593
*/
 
594
 
 
595
bool trans_xa_end(THD *thd)
 
596
{
 
597
  DBUG_ENTER("trans_xa_end");
 
598
 
 
599
  /* TODO: SUSPEND and FOR MIGRATE are not supported yet. */
 
600
  if (thd->lex->xa_opt != XA_NONE)
 
601
    my_error(ER_XAER_INVAL, MYF(0));
 
602
  else if (thd->transaction.xid_state.xa_state != XA_ACTIVE)
 
603
    my_error(ER_XAER_RMFAIL, MYF(0),
 
604
             xa_state_names[thd->transaction.xid_state.xa_state]);
 
605
  else if (!thd->transaction.xid_state.xid.eq(thd->lex->xid))
 
606
    my_error(ER_XAER_NOTA, MYF(0));
 
607
  else if (!xa_trans_rolled_back(&thd->transaction.xid_state))
 
608
    thd->transaction.xid_state.xa_state= XA_IDLE;
 
609
 
 
610
  DBUG_RETURN(thd->is_error() ||
 
611
              thd->transaction.xid_state.xa_state != XA_IDLE);
 
612
}
 
613
 
 
614
 
 
615
/**
 
616
  Put a XA transaction in the PREPARED state.
 
617
 
 
618
  @param thd    Current thread
 
619
 
 
620
  @retval FALSE  Success
 
621
  @retval TRUE   Failure
 
622
*/
 
623
 
 
624
bool trans_xa_prepare(THD *thd)
 
625
{
 
626
  DBUG_ENTER("trans_xa_prepare");
 
627
 
 
628
  if (thd->transaction.xid_state.xa_state != XA_IDLE)
 
629
    my_error(ER_XAER_RMFAIL, MYF(0),
 
630
             xa_state_names[thd->transaction.xid_state.xa_state]);
 
631
  else if (!thd->transaction.xid_state.xid.eq(thd->lex->xid))
 
632
    my_error(ER_XAER_NOTA, MYF(0));
 
633
  else if (ha_prepare(thd))
 
634
  {
 
635
    xid_cache_delete(&thd->transaction.xid_state);
 
636
    thd->transaction.xid_state.xa_state= XA_NOTR;
 
637
    my_error(ER_XA_RBROLLBACK, MYF(0));
 
638
  }
 
639
  else
 
640
    thd->transaction.xid_state.xa_state= XA_PREPARED;
 
641
 
 
642
  DBUG_RETURN(thd->is_error() ||
 
643
              thd->transaction.xid_state.xa_state != XA_PREPARED);
 
644
}
 
645
 
 
646
 
 
647
/**
 
648
  Commit and terminate the a XA transaction.
 
649
 
 
650
  @param thd    Current thread
 
651
 
 
652
  @retval FALSE  Success
 
653
  @retval TRUE   Failure
 
654
*/
 
655
 
 
656
bool trans_xa_commit(THD *thd)
 
657
{
 
658
  bool res= TRUE;
 
659
  enum xa_states xa_state= thd->transaction.xid_state.xa_state;
 
660
  DBUG_ENTER("trans_xa_commit");
 
661
 
 
662
  if (!thd->transaction.xid_state.xid.eq(thd->lex->xid))
 
663
  {
 
664
    XID_STATE *xs= xid_cache_search(thd->lex->xid);
 
665
    res= !xs || xs->in_thd;
 
666
    if (res)
 
667
      my_error(ER_XAER_NOTA, MYF(0));
 
668
    else
 
669
    {
 
670
      res= xa_trans_rolled_back(xs);
 
671
      ha_commit_or_rollback_by_xid(thd->lex->xid, !res);
 
672
      xid_cache_delete(xs);
 
673
    }
 
674
    DBUG_RETURN(res);
 
675
  }
 
676
 
 
677
  if (xa_trans_rolled_back(&thd->transaction.xid_state))
 
678
  {
 
679
    xa_trans_force_rollback(thd);
 
680
    res= thd->is_error();
 
681
  }
 
682
  else if (xa_state == XA_IDLE && thd->lex->xa_opt == XA_ONE_PHASE)
 
683
  {
 
684
    int r= ha_commit_trans(thd, TRUE);
 
685
    if ((res= test(r)))
 
686
      my_error(r == 1 ? ER_XA_RBROLLBACK : ER_XAER_RMERR, MYF(0));
 
687
  }
 
688
  else if (xa_state == XA_PREPARED && thd->lex->xa_opt == XA_NONE)
 
689
  {
 
690
    MDL_request mdl_request;
 
691
 
 
692
    /*
 
693
      Acquire metadata lock which will ensure that COMMIT is blocked
 
694
      by active FLUSH TABLES WITH READ LOCK (and vice versa COMMIT in
 
695
      progress blocks FTWRL).
 
696
 
 
697
      We allow FLUSHer to COMMIT; we assume FLUSHer knows what it does.
 
698
    */
 
699
    mdl_request.init(MDL_key::COMMIT, "", "", MDL_INTENTION_EXCLUSIVE,
 
700
                     MDL_TRANSACTION);
 
701
 
 
702
    if (thd->mdl_context.acquire_lock(&mdl_request,
 
703
                                      thd->variables.lock_wait_timeout))
 
704
    {
 
705
      ha_rollback_trans(thd, TRUE);
 
706
      my_error(ER_XAER_RMERR, MYF(0));
 
707
    }
 
708
    else
 
709
    {
 
710
      DEBUG_SYNC(thd, "trans_xa_commit_after_acquire_commit_lock");
 
711
 
 
712
      res= test(ha_commit_one_phase(thd, 1));
 
713
      if (res)
 
714
        my_error(ER_XAER_RMERR, MYF(0));
 
715
    }
 
716
  }
 
717
  else
 
718
  {
 
719
    my_error(ER_XAER_RMFAIL, MYF(0), xa_state_names[xa_state]);
 
720
    DBUG_RETURN(TRUE);
 
721
  }
 
722
 
 
723
  thd->variables.option_bits&= ~(OPTION_BEGIN | OPTION_KEEP_LOG);
 
724
  thd->transaction.all.modified_non_trans_table= FALSE;
 
725
  thd->server_status&= ~SERVER_STATUS_IN_TRANS;
 
726
  xid_cache_delete(&thd->transaction.xid_state);
 
727
  thd->transaction.xid_state.xa_state= XA_NOTR;
 
728
 
 
729
  DBUG_RETURN(res);
 
730
}
 
731
 
 
732
 
 
733
/**
 
734
  Roll back and terminate a XA transaction.
 
735
 
 
736
  @param thd    Current thread
 
737
 
 
738
  @retval FALSE  Success
 
739
  @retval TRUE   Failure
 
740
*/
 
741
 
 
742
bool trans_xa_rollback(THD *thd)
 
743
{
 
744
  bool res= TRUE;
 
745
  enum xa_states xa_state= thd->transaction.xid_state.xa_state;
 
746
  DBUG_ENTER("trans_xa_rollback");
 
747
 
 
748
  if (!thd->transaction.xid_state.xid.eq(thd->lex->xid))
 
749
  {
 
750
    XID_STATE *xs= xid_cache_search(thd->lex->xid);
 
751
    if (!xs || xs->in_thd)
 
752
      my_error(ER_XAER_NOTA, MYF(0));
 
753
    else
 
754
    {
 
755
      xa_trans_rolled_back(xs);
 
756
      ha_commit_or_rollback_by_xid(thd->lex->xid, 0);
 
757
      xid_cache_delete(xs);
 
758
    }
 
759
    DBUG_RETURN(thd->stmt_da->is_error());
 
760
  }
 
761
 
 
762
  if (xa_state != XA_IDLE && xa_state != XA_PREPARED && xa_state != XA_ROLLBACK_ONLY)
 
763
  {
 
764
    my_error(ER_XAER_RMFAIL, MYF(0), xa_state_names[xa_state]);
 
765
    DBUG_RETURN(TRUE);
 
766
  }
 
767
 
 
768
  res= xa_trans_force_rollback(thd);
 
769
 
 
770
  thd->variables.option_bits&= ~(OPTION_BEGIN | OPTION_KEEP_LOG);
 
771
  thd->transaction.all.modified_non_trans_table= FALSE;
 
772
  thd->server_status&= ~SERVER_STATUS_IN_TRANS;
 
773
  xid_cache_delete(&thd->transaction.xid_state);
 
774
  thd->transaction.xid_state.xa_state= XA_NOTR;
 
775
 
 
776
  DBUG_RETURN(res);
 
777
}