1
/*****************************************************************************
3
Copyright (c) 1996, 2010, Innobase Oy. All Rights Reserved.
5
This program is free software; you can redistribute it and/or modify it under
6
the terms of the GNU General Public License as published by the Free Software
7
Foundation; version 2 of the License.
9
This program is distributed in the hope that it will be useful, but WITHOUT
10
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
13
You should have received a copy of the GNU General Public License along with
14
this program; if not, write to the Free Software Foundation, Inc., 51 Franklin
15
St, Fifth Floor, Boston, MA 02110-1301 USA
17
*****************************************************************************/
19
/**************************************************//**
23
Created 3/26/1996 Heikki Tuuri
24
*******************************************************/
36
#include "lock0lock.h"
39
#include "read0read.h"
45
#include "ha_prototypes.h"
47
/** Dummy session used currently in MySQL interface */
48
UNIV_INTERN sess_t* trx_dummy_sess = NULL;
50
/** Number of transactions currently allocated for MySQL: protected by
52
UNIV_INTERN ulint trx_n_mysql_transactions = 0;
55
/* Key to register the mutex with performance schema */
56
UNIV_INTERN mysql_pfs_key_t trx_undo_mutex_key;
57
#endif /* UNIV_PFS_MUTEX */
59
/*************************************************************//**
60
Set detailed error message for the transaction. */
63
trx_set_detailed_error(
64
/*===================*/
65
trx_t* trx, /*!< in: transaction struct */
66
const char* msg) /*!< in: detailed error message */
68
ut_strlcpy(trx->detailed_error, msg, sizeof(trx->detailed_error));
71
/*************************************************************//**
72
Set detailed error message for the transaction from a file. Note that the
73
file is rewinded before reading from it. */
76
trx_set_detailed_error_from_file(
77
/*=============================*/
78
trx_t* trx, /*!< in: transaction struct */
79
FILE* file) /*!< in: file to read message from */
81
os_file_read_string(file, trx->detailed_error,
82
sizeof(trx->detailed_error));
85
/****************************************************************//**
86
Creates and initializes a transaction object.
87
@return own: the transaction */
92
sess_t* sess) /*!< in: session */
96
ut_ad(mutex_own(&kernel_mutex));
99
trx = mem_alloc(sizeof(trx_t));
101
trx->magic_n = TRX_MAGIC_N;
106
trx->is_recovered = 0;
107
trx->conc_state = TRX_NOT_STARTED;
108
trx->start_time = time(NULL);
110
trx->isolation_level = TRX_ISO_REPEATABLE_READ;
113
trx->no = IB_ULONGLONG_MAX;
115
trx->support_xa = TRUE;
117
trx->check_foreigns = TRUE;
118
trx->check_unique_secondary = TRUE;
120
trx->flush_log_later = FALSE;
121
trx->must_flush_log_later = FALSE;
123
trx->dict_operation = TRX_DICT_OP_NONE;
126
trx->mysql_thd = NULL;
127
trx->active_trans = 0;
130
trx->mysql_n_tables_locked = 0;
132
trx->mysql_log_file_name = NULL;
133
trx->mysql_log_offset = 0;
135
mutex_create(trx_undo_mutex_key, &trx->undo_mutex, SYNC_TRX_UNDO);
140
trx->last_sql_stat_start.least_undo_no = 0;
141
trx->insert_undo = NULL;
142
trx->update_undo = NULL;
143
trx->undo_no_arr = NULL;
145
trx->error_state = DB_SUCCESS;
146
trx->error_key_num = 0;
147
trx->detailed_error[0] = '\0';
150
trx->que_state = TRX_QUE_RUNNING;
151
trx->n_active_thrs = 0;
153
trx->handling_signals = FALSE;
155
UT_LIST_INIT(trx->signals);
156
UT_LIST_INIT(trx->reply_signals);
160
trx->wait_lock = NULL;
161
trx->was_chosen_as_deadlock_victim = FALSE;
162
UT_LIST_INIT(trx->wait_thrs);
164
trx->lock_heap = mem_heap_create_in_buffer(256);
165
UT_LIST_INIT(trx->trx_locks);
167
UT_LIST_INIT(trx->trx_savepoints);
169
trx->dict_operation_lock_mode = 0;
170
trx->has_search_latch = FALSE;
171
trx->search_latch_timeout = BTR_SEA_TIMEOUT;
173
trx->declared_to_be_inside_innodb = FALSE;
174
trx->n_tickets_to_enter_innodb = 0;
176
trx->global_read_view_heap = mem_heap_create(256);
177
trx->global_read_view = NULL;
178
trx->read_view = NULL;
180
/* Set X/Open XA transaction identification to NULL */
181
memset(&trx->xid, 0, sizeof(trx->xid));
182
trx->xid.formatID = -1;
184
trx->n_autoinc_rows = 0;
186
/* Remember to free the vector explicitly. */
187
trx->autoinc_locks = ib_vector_create(
188
mem_heap_create(sizeof(ib_vector_t) + sizeof(void*) * 4), 4);
193
/********************************************************************//**
194
Creates a transaction object for MySQL.
195
@return own: transaction object */
198
trx_allocate_for_mysql(void)
199
/*========================*/
203
mutex_enter(&kernel_mutex);
205
trx = trx_create(trx_dummy_sess);
207
trx_n_mysql_transactions++;
209
UT_LIST_ADD_FIRST(mysql_trx_list, trx_sys->mysql_trx_list, trx);
211
mutex_exit(&kernel_mutex);
213
trx->mysql_thread_id = os_thread_get_curr_id();
215
trx->mysql_process_no = os_proc_get_number();
220
/********************************************************************//**
221
Creates a transaction object for background operations by the master thread.
222
@return own: transaction object */
225
trx_allocate_for_background(void)
226
/*=============================*/
230
mutex_enter(&kernel_mutex);
232
trx = trx_create(trx_dummy_sess);
234
mutex_exit(&kernel_mutex);
239
/********************************************************************//**
240
Releases the search latch if trx has reserved it. */
243
trx_search_latch_release_if_reserved(
244
/*=================================*/
245
trx_t* trx) /*!< in: transaction */
247
if (trx->has_search_latch) {
248
rw_lock_s_unlock(&btr_search_latch);
250
trx->has_search_latch = FALSE;
254
/********************************************************************//**
255
Frees a transaction object. */
260
trx_t* trx) /*!< in, own: trx object */
262
ut_ad(mutex_own(&kernel_mutex));
264
if (trx->declared_to_be_inside_innodb) {
265
ut_print_timestamp(stderr);
266
fputs(" InnoDB: Error: Freeing a trx which is declared"
267
" to be processing\n"
268
"InnoDB: inside InnoDB.\n", stderr);
269
trx_print(stderr, trx, 600);
272
/* This is an error but not a fatal error. We must keep
273
the counters like srv_conc_n_threads accurate. */
274
srv_conc_force_exit_innodb(trx);
277
if (trx->mysql_n_tables_locked != 0) {
279
ut_print_timestamp(stderr);
281
" InnoDB: Error: MySQL is freeing a thd\n"
282
"InnoDB: and trx->mysql_n_tables_locked is %lu.\n",
283
(ulong)trx->mysql_n_tables_locked);
285
trx_print(stderr, trx, 600);
287
ut_print_buf(stderr, trx, sizeof(trx_t));
291
ut_a(trx->magic_n == TRX_MAGIC_N);
293
trx->magic_n = 11112222;
295
ut_a(trx->conc_state == TRX_NOT_STARTED);
297
mutex_free(&(trx->undo_mutex));
299
ut_a(trx->insert_undo == NULL);
300
ut_a(trx->update_undo == NULL);
302
if (trx->undo_no_arr) {
303
trx_undo_arr_free(trx->undo_no_arr);
306
ut_a(UT_LIST_GET_LEN(trx->signals) == 0);
307
ut_a(UT_LIST_GET_LEN(trx->reply_signals) == 0);
309
ut_a(trx->wait_lock == NULL);
310
ut_a(UT_LIST_GET_LEN(trx->wait_thrs) == 0);
312
ut_a(!trx->has_search_latch);
314
ut_a(trx->dict_operation_lock_mode == 0);
316
if (trx->lock_heap) {
317
mem_heap_free(trx->lock_heap);
320
ut_a(UT_LIST_GET_LEN(trx->trx_locks) == 0);
322
if (trx->global_read_view_heap) {
323
mem_heap_free(trx->global_read_view_heap);
326
trx->global_read_view = NULL;
328
ut_a(trx->read_view == NULL);
330
ut_a(ib_vector_is_empty(trx->autoinc_locks));
331
/* We allocated a dedicated heap for the vector. */
332
ib_vector_free(trx->autoinc_locks);
337
/********************************************************************//**
338
Frees a transaction object for MySQL. */
343
trx_t* trx) /*!< in, own: trx object */
345
mutex_enter(&kernel_mutex);
347
UT_LIST_REMOVE(mysql_trx_list, trx_sys->mysql_trx_list, trx);
351
ut_a(trx_n_mysql_transactions > 0);
353
trx_n_mysql_transactions--;
355
mutex_exit(&kernel_mutex);
358
/********************************************************************//**
359
Frees a transaction object of a background operation of the master thread. */
362
trx_free_for_background(
363
/*====================*/
364
trx_t* trx) /*!< in, own: trx object */
366
mutex_enter(&kernel_mutex);
370
mutex_exit(&kernel_mutex);
373
/****************************************************************//**
374
Inserts the trx handle in the trx system trx list in the right position.
375
The list is sorted on the trx id so that the biggest id is at the list
376
start. This function is used at the database startup to insert incomplete
377
transactions to the list. */
380
trx_list_insert_ordered(
381
/*====================*/
382
trx_t* trx) /*!< in: trx handle */
386
ut_ad(mutex_own(&kernel_mutex));
388
trx2 = UT_LIST_GET_FIRST(trx_sys->trx_list);
390
while (trx2 != NULL) {
391
if (trx->id >= trx2->id) {
393
ut_ad(trx->id > trx2->id);
396
trx2 = UT_LIST_GET_NEXT(trx_list, trx2);
400
trx2 = UT_LIST_GET_PREV(trx_list, trx2);
403
UT_LIST_ADD_FIRST(trx_list, trx_sys->trx_list, trx);
405
UT_LIST_INSERT_AFTER(trx_list, trx_sys->trx_list,
409
UT_LIST_ADD_LAST(trx_list, trx_sys->trx_list, trx);
413
/****************************************************************//**
414
Creates trx objects for transactions and initializes the trx list of
415
trx_sys at database start. Rollback segment and undo log lists must
416
already exist when this function is called, because the lists of
417
transactions to be rolled back or cleaned up are built based on the
421
trx_lists_init_at_db_start(void)
422
/*============================*/
428
ut_ad(mutex_own(&kernel_mutex));
429
UT_LIST_INIT(trx_sys->trx_list);
431
/* Look from the rollback segments if there exist undo logs for
434
rseg = UT_LIST_GET_FIRST(trx_sys->rseg_list);
436
while (rseg != NULL) {
437
undo = UT_LIST_GET_FIRST(rseg->insert_undo_list);
439
while (undo != NULL) {
441
trx = trx_create(trx_dummy_sess);
443
trx->is_recovered = TRUE;
444
trx->id = undo->trx_id;
445
trx->xid = undo->xid;
446
trx->insert_undo = undo;
449
if (undo->state != TRX_UNDO_ACTIVE) {
451
/* Prepared transactions are left in
452
the prepared state waiting for a
453
commit or abort decision from MySQL */
455
if (undo->state == TRX_UNDO_PREPARED) {
458
"InnoDB: Transaction "
461
" XA prepared state.\n",
464
if (srv_force_recovery == 0) {
466
trx->conc_state = TRX_PREPARED;
470
" innodb_force_recovery"
475
trx->conc_state = TRX_ACTIVE;
479
= TRX_COMMITTED_IN_MEMORY;
482
/* We give a dummy value for the trx no;
483
this should have no relevance since purge
484
is not interested in committed transaction
485
numbers, unless they are in the history
486
list, in which case it looks the number
487
from the disk based undo log structure */
491
trx->conc_state = TRX_ACTIVE;
493
/* A running transaction always has the number
494
field inited to IB_ULONGLONG_MAX */
496
trx->no = IB_ULONGLONG_MAX;
499
if (undo->dict_operation) {
500
trx_set_dict_operation(
501
trx, TRX_DICT_OP_TABLE);
502
trx->table_id = undo->table_id;
506
trx->undo_no = undo->top_undo_no + 1;
509
trx_list_insert_ordered(trx);
511
undo = UT_LIST_GET_NEXT(undo_list, undo);
514
undo = UT_LIST_GET_FIRST(rseg->update_undo_list);
516
while (undo != NULL) {
517
trx = trx_get_on_id(undo->trx_id);
520
trx = trx_create(trx_dummy_sess);
522
trx->is_recovered = TRUE;
523
trx->id = undo->trx_id;
524
trx->xid = undo->xid;
526
if (undo->state != TRX_UNDO_ACTIVE) {
528
/* Prepared transactions are left in
529
the prepared state waiting for a
530
commit or abort decision from MySQL */
532
if (undo->state == TRX_UNDO_PREPARED) {
534
"InnoDB: Transaction "
535
TRX_ID_FMT " was in the"
536
" XA prepared state.\n",
539
if (srv_force_recovery == 0) {
546
" innodb_force_recovery"
556
= TRX_COMMITTED_IN_MEMORY;
559
/* We give a dummy value for the trx
564
trx->conc_state = TRX_ACTIVE;
566
/* A running transaction always has
567
the number field inited to
570
trx->no = IB_ULONGLONG_MAX;
574
trx_list_insert_ordered(trx);
576
if (undo->dict_operation) {
577
trx_set_dict_operation(
578
trx, TRX_DICT_OP_TABLE);
579
trx->table_id = undo->table_id;
583
trx->update_undo = undo;
586
&& undo->top_undo_no >= trx->undo_no) {
588
trx->undo_no = undo->top_undo_no + 1;
591
undo = UT_LIST_GET_NEXT(undo_list, undo);
594
rseg = UT_LIST_GET_NEXT(rseg_list, rseg);
598
/******************************************************************//**
599
Assigns a rollback segment to a transaction in a round-robin fashion.
600
Skips the SYSTEM rollback segment if another is available.
601
@return assigned rollback segment id */
604
trx_assign_rseg(void)
605
/*=================*/
607
trx_rseg_t* rseg = trx_sys->latest_rseg;
609
ut_ad(mutex_own(&kernel_mutex));
611
/* Get next rseg in a round-robin fashion */
613
rseg = UT_LIST_GET_NEXT(rseg_list, rseg);
616
rseg = UT_LIST_GET_FIRST(trx_sys->rseg_list);
619
/* If it is the SYSTEM rollback segment, and there exist others, skip
622
if ((rseg->id == TRX_SYS_SYSTEM_RSEG_ID)
623
&& (UT_LIST_GET_LEN(trx_sys->rseg_list) > 1)) {
627
trx_sys->latest_rseg = rseg;
632
/****************************************************************//**
633
Starts a new transaction.
639
trx_t* trx, /*!< in: transaction */
640
ulint rseg_id)/*!< in: rollback segment id; if ULINT_UNDEFINED
641
is passed, the system chooses the rollback segment
642
automatically in a round-robin fashion */
646
ut_ad(mutex_own(&kernel_mutex));
647
ut_ad(trx->rseg == NULL);
651
trx->conc_state = TRX_ACTIVE;
652
trx->start_time = time(NULL);
657
ut_ad(trx->conc_state != TRX_ACTIVE);
659
if (rseg_id == ULINT_UNDEFINED) {
661
rseg_id = trx_assign_rseg();
664
rseg = trx_sys_get_nth_rseg(trx_sys, rseg_id);
666
trx->id = trx_sys_get_new_trx_id();
668
/* The initial value for trx->no: IB_ULONGLONG_MAX is used in
669
read_view_open_now: */
671
trx->no = IB_ULONGLONG_MAX;
675
trx->conc_state = TRX_ACTIVE;
676
trx->start_time = time(NULL);
678
UT_LIST_ADD_FIRST(trx_list, trx_sys->trx_list, trx);
683
/****************************************************************//**
684
Starts a new transaction.
690
trx_t* trx, /*!< in: transaction */
691
ulint rseg_id)/*!< in: rollback segment id; if ULINT_UNDEFINED
692
is passed, the system chooses the rollback segment
693
automatically in a round-robin fashion */
697
/* Update the info whether we should skip XA steps that eat CPU time
698
For the duration of the transaction trx->support_xa is not reread
699
from thd so any changes in the value take effect in the next
700
transaction. This is to avoid a scenario where some undo
701
generated by a transaction, has XA stuff, and other undo,
702
generated by the same transaction, doesn't. */
703
trx->support_xa = thd_supports_xa(trx->mysql_thd);
705
mutex_enter(&kernel_mutex);
707
ret = trx_start_low(trx, rseg_id);
709
mutex_exit(&kernel_mutex);
714
/****************************************************************//**
715
Commits a transaction. */
718
trx_commit_off_kernel(
719
/*==================*/
720
trx_t* trx) /*!< in: transaction */
722
page_t* update_hdr_page;
728
ut_ad(mutex_own(&kernel_mutex));
730
trx->must_flush_log_later = FALSE;
734
if (trx->insert_undo != NULL || trx->update_undo != NULL) {
736
mutex_exit(&kernel_mutex);
740
/* Change the undo log segment states from TRX_UNDO_ACTIVE
741
to some other state: these modifications to the file data
742
structure define the transaction as committed in the file
743
based world, at the serialization point of the log sequence
744
number lsn obtained below. */
746
mutex_enter(&(rseg->mutex));
748
if (trx->insert_undo != NULL) {
749
trx_undo_set_state_at_finish(
750
rseg, trx, trx->insert_undo, &mtr);
753
undo = trx->update_undo;
756
mutex_enter(&kernel_mutex);
757
trx->no = trx_sys_get_new_trx_no();
758
mutex_exit(&kernel_mutex);
760
/* It is not necessary to obtain trx->undo_mutex here
761
because only a single OS thread is allowed to do the
762
transaction commit for this transaction. */
764
update_hdr_page = trx_undo_set_state_at_finish(
765
rseg, trx, undo, &mtr);
767
/* We have to do the cleanup for the update log while
768
holding the rseg mutex because update log headers
769
have to be put to the history list in the order of
772
trx_undo_update_cleanup(trx, update_hdr_page, &mtr);
775
mutex_exit(&(rseg->mutex));
777
/* Update the latest MySQL binlog name and offset info
778
in trx sys header if MySQL binlogging is on or the database
779
server is a MySQL replication slave */
781
if (trx->mysql_log_file_name
782
&& trx->mysql_log_file_name[0] != '\0') {
783
trx_sys_update_mysql_binlog_offset(
784
trx->mysql_log_file_name,
785
trx->mysql_log_offset,
786
TRX_SYS_MYSQL_LOG_INFO, &mtr);
787
trx->mysql_log_file_name = NULL;
790
/* The following call commits the mini-transaction, making the
791
whole transaction committed in the file-based world, at this
792
log sequence number. The transaction becomes 'durable' when
793
we write the log to disk, but in the logical sense the commit
794
in the file-based data structures (undo logs etc.) happens
797
NOTE that transaction numbers, which are assigned only to
798
transactions with an update undo log, do not necessarily come
799
in exactly the same order as commit lsn's, if the transactions
800
have different rollback segments. To get exactly the same
801
order we should hold the kernel mutex up to this point,
802
adding to the contention of the kernel mutex. However, if
803
a transaction T2 is able to see modifications made by
804
a transaction T1, T2 will always get a bigger transaction
805
number and a bigger commit lsn than T1. */
812
mutex_enter(&kernel_mutex);
815
ut_ad(trx->conc_state == TRX_ACTIVE
816
|| trx->conc_state == TRX_PREPARED);
817
ut_ad(mutex_own(&kernel_mutex));
819
/* The following assignment makes the transaction committed in memory
820
and makes its changes to data visible to other transactions.
821
NOTE that there is a small discrepancy from the strict formal
822
visibility rules here: a human user of the database can see
823
modifications made by another transaction T even before the necessary
824
log segment has been flushed to the disk. If the database happens to
825
crash before the flush, the user has seen modifications from T which
826
will never be a committed transaction. However, any transaction T2
827
which sees the modifications of the committing transaction T, and
828
which also itself makes modifications to the database, will get an lsn
829
larger than the committing transaction T. In the case where the log
830
flush fails, and T never gets committed, also T2 will never get
833
/*--------------------------------------*/
834
trx->conc_state = TRX_COMMITTED_IN_MEMORY;
835
/*--------------------------------------*/
837
/* If we release kernel_mutex below and we are still doing
838
recovery i.e.: back ground rollback thread is still active
839
then there is a chance that the rollback thread may see
840
this trx as COMMITTED_IN_MEMORY and goes adhead to clean it
841
up calling trx_cleanup_at_db_startup(). This can happen
842
in the case we are committing a trx here that is left in
843
PREPARED state during the crash. Note that commit of the
844
rollback of a PREPARED trx happens in the recovery thread
845
while the rollback of other transactions happen in the
846
background thread. To avoid this race we unconditionally
847
unset the is_recovered flag from the trx. */
849
trx->is_recovered = FALSE;
851
lock_release_off_kernel(trx);
853
if (trx->global_read_view) {
854
read_view_close(trx->global_read_view);
855
mem_heap_empty(trx->global_read_view_heap);
856
trx->global_read_view = NULL;
859
trx->read_view = NULL;
863
mutex_exit(&kernel_mutex);
865
if (trx->insert_undo != NULL) {
867
trx_undo_insert_cleanup(trx);
870
/* NOTE that we could possibly make a group commit more
871
efficient here: call os_thread_yield here to allow also other
872
trxs to come to commit! */
874
/*-------------------------------------*/
876
/* Depending on the my.cnf options, we may now write the log
877
buffer to the log files, making the transaction durable if
878
the OS does not crash. We may also flush the log files to
879
disk, making the transaction durable also at an OS crash or a
882
The idea in InnoDB's group commit is that a group of
883
transactions gather behind a trx doing a physical disk write
884
to log files, and when that physical write has been completed,
885
one of those transactions does a write which commits the whole
886
group. Note that this group commit will only bring benefit if
887
there are > 2 users in the database. Then at least 2 users can
888
gather behind one doing the physical log write to disk.
890
If we are calling trx_commit() under prepare_commit_mutex, we
891
will delay possible log write and flush to a separate function
892
trx_commit_complete_for_mysql(), which is only called when the
893
thread has released the mutex. This is to make the
894
group commit algorithm to work. Otherwise, the prepare_commit
895
mutex would serialize all commits and prevent a group of
896
transactions from gathering. */
898
if (trx->flush_log_later) {
900
trx->must_flush_log_later = TRUE;
901
} else if (srv_flush_log_at_trx_commit == 0) {
903
} else if (srv_flush_log_at_trx_commit == 1) {
904
if (srv_unix_file_flush_method == SRV_UNIX_NOSYNC) {
905
/* Write the log but do not flush it to disk */
907
log_write_up_to(lsn, LOG_WAIT_ONE_GROUP,
910
/* Write the log to the log files AND flush
913
log_write_up_to(lsn, LOG_WAIT_ONE_GROUP, TRUE);
915
} else if (srv_flush_log_at_trx_commit == 2) {
917
/* Write the log but do not flush it to disk */
919
log_write_up_to(lsn, LOG_WAIT_ONE_GROUP, FALSE);
924
trx->commit_lsn = lsn;
926
/*-------------------------------------*/
928
mutex_enter(&kernel_mutex);
931
/* Free all savepoints */
932
trx_roll_free_all_savepoints(trx);
934
trx->conc_state = TRX_NOT_STARTED;
937
trx->last_sql_stat_start.least_undo_no = 0;
939
ut_ad(UT_LIST_GET_LEN(trx->wait_thrs) == 0);
940
ut_ad(UT_LIST_GET_LEN(trx->trx_locks) == 0);
942
UT_LIST_REMOVE(trx_list, trx_sys->trx_list, trx);
945
/****************************************************************//**
946
Cleans up a transaction at database startup. The cleanup is needed if
947
the transaction already got to the middle of a commit when the database
948
crashed, and we cannot roll it back. */
951
trx_cleanup_at_db_startup(
952
/*======================*/
953
trx_t* trx) /*!< in: transaction */
955
if (trx->insert_undo != NULL) {
957
trx_undo_insert_cleanup(trx);
960
trx->conc_state = TRX_NOT_STARTED;
963
trx->last_sql_stat_start.least_undo_no = 0;
965
UT_LIST_REMOVE(trx_list, trx_sys->trx_list, trx);
968
/********************************************************************//**
969
Assigns a read view for a consistent read query. All the consistent reads
970
within the same transaction will get the same read view, which is created
971
when this function is first called for a new started transaction.
972
@return consistent read view */
975
trx_assign_read_view(
976
/*=================*/
977
trx_t* trx) /*!< in: active transaction */
979
ut_ad(trx->conc_state == TRX_ACTIVE);
981
if (trx->read_view) {
982
return(trx->read_view);
985
mutex_enter(&kernel_mutex);
987
if (!trx->read_view) {
988
trx->read_view = read_view_open_now(
989
trx->id, trx->global_read_view_heap);
990
trx->global_read_view = trx->read_view;
993
mutex_exit(&kernel_mutex);
995
return(trx->read_view);
998
/****************************************************************//**
999
Commits a transaction. NOTE that the kernel mutex is temporarily released. */
1002
trx_handle_commit_sig_off_kernel(
1003
/*=============================*/
1004
trx_t* trx, /*!< in: transaction */
1005
que_thr_t** next_thr) /*!< in/out: next query thread to run;
1006
if the value which is passed in is
1007
a pointer to a NULL pointer, then the
1008
calling function can start running
1009
a new query thread */
1012
trx_sig_t* next_sig;
1014
ut_ad(mutex_own(&kernel_mutex));
1016
trx->que_state = TRX_QUE_COMMITTING;
1018
trx_commit_off_kernel(trx);
1020
ut_ad(UT_LIST_GET_LEN(trx->wait_thrs) == 0);
1022
/* Remove all TRX_SIG_COMMIT signals from the signal queue and send
1023
reply messages to them */
1025
sig = UT_LIST_GET_FIRST(trx->signals);
1027
while (sig != NULL) {
1028
next_sig = UT_LIST_GET_NEXT(signals, sig);
1030
if (sig->type == TRX_SIG_COMMIT) {
1032
trx_sig_reply(sig, next_thr);
1033
trx_sig_remove(trx, sig);
1039
trx->que_state = TRX_QUE_RUNNING;
1042
/***********************************************************//**
1043
The transaction must be in the TRX_QUE_LOCK_WAIT state. Puts it to
1044
the TRX_QUE_RUNNING state and releases query threads which were
1045
waiting for a lock in the wait_thrs list. */
1050
trx_t* trx) /*!< in: transaction */
1054
ut_ad(mutex_own(&kernel_mutex));
1055
ut_ad(trx->que_state == TRX_QUE_LOCK_WAIT);
1057
thr = UT_LIST_GET_FIRST(trx->wait_thrs);
1059
while (thr != NULL) {
1060
que_thr_end_wait_no_next_thr(thr);
1062
UT_LIST_REMOVE(trx_thrs, trx->wait_thrs, thr);
1064
thr = UT_LIST_GET_FIRST(trx->wait_thrs);
1067
trx->que_state = TRX_QUE_RUNNING;
1070
/***********************************************************//**
1071
Moves the query threads in the lock wait list to the SUSPENDED state and puts
1072
the transaction to the TRX_QUE_RUNNING state. */
1075
trx_lock_wait_to_suspended(
1076
/*=======================*/
1077
trx_t* trx) /*!< in: transaction in the TRX_QUE_LOCK_WAIT state */
1081
ut_ad(mutex_own(&kernel_mutex));
1082
ut_ad(trx->que_state == TRX_QUE_LOCK_WAIT);
1084
thr = UT_LIST_GET_FIRST(trx->wait_thrs);
1086
while (thr != NULL) {
1087
thr->state = QUE_THR_SUSPENDED;
1089
UT_LIST_REMOVE(trx_thrs, trx->wait_thrs, thr);
1091
thr = UT_LIST_GET_FIRST(trx->wait_thrs);
1094
trx->que_state = TRX_QUE_RUNNING;
1097
/***********************************************************//**
1098
Moves the query threads in the sig reply wait list of trx to the SUSPENDED
1102
trx_sig_reply_wait_to_suspended(
1103
/*============================*/
1104
trx_t* trx) /*!< in: transaction */
1109
ut_ad(mutex_own(&kernel_mutex));
1111
sig = UT_LIST_GET_FIRST(trx->reply_signals);
1113
while (sig != NULL) {
1114
thr = sig->receiver;
1116
ut_ad(thr->state == QUE_THR_SIG_REPLY_WAIT);
1118
thr->state = QUE_THR_SUSPENDED;
1120
sig->receiver = NULL;
1122
UT_LIST_REMOVE(reply_signals, trx->reply_signals, sig);
1124
sig = UT_LIST_GET_FIRST(trx->reply_signals);
1128
/*****************************************************************//**
1129
Checks the compatibility of a new signal with the other signals in the
1131
@return TRUE if the signal can be queued */
1134
trx_sig_is_compatible(
1135
/*==================*/
1136
trx_t* trx, /*!< in: trx handle */
1137
ulint type, /*!< in: signal type */
1138
ulint sender) /*!< in: TRX_SIG_SELF or TRX_SIG_OTHER_SESS */
1142
ut_ad(mutex_own(&kernel_mutex));
1144
if (UT_LIST_GET_LEN(trx->signals) == 0) {
1149
if (sender == TRX_SIG_SELF) {
1150
if (type == TRX_SIG_ERROR_OCCURRED) {
1154
} else if (type == TRX_SIG_BREAK_EXECUTION) {
1162
ut_ad(sender == TRX_SIG_OTHER_SESS);
1164
sig = UT_LIST_GET_FIRST(trx->signals);
1166
if (type == TRX_SIG_COMMIT) {
1167
while (sig != NULL) {
1169
if (sig->type == TRX_SIG_TOTAL_ROLLBACK) {
1174
sig = UT_LIST_GET_NEXT(signals, sig);
1179
} else if (type == TRX_SIG_TOTAL_ROLLBACK) {
1180
while (sig != NULL) {
1182
if (sig->type == TRX_SIG_COMMIT) {
1187
sig = UT_LIST_GET_NEXT(signals, sig);
1192
} else if (type == TRX_SIG_BREAK_EXECUTION) {
1202
/****************************************************************//**
1203
Sends a signal to a trx object. */
1208
trx_t* trx, /*!< in: trx handle */
1209
ulint type, /*!< in: signal type */
1210
ulint sender, /*!< in: TRX_SIG_SELF or
1211
TRX_SIG_OTHER_SESS */
1212
que_thr_t* receiver_thr, /*!< in: query thread which wants the
1213
reply, or NULL; if type is
1214
TRX_SIG_END_WAIT, this must be NULL */
1215
trx_savept_t* savept, /*!< in: possible rollback savepoint, or
1217
que_thr_t** next_thr) /*!< in/out: next query thread to run;
1218
if the value which is passed in is
1219
a pointer to a NULL pointer, then the
1220
calling function can start running
1221
a new query thread; if the parameter
1222
is NULL, it is ignored */
1225
trx_t* receiver_trx;
1228
ut_ad(mutex_own(&kernel_mutex));
1230
if (!trx_sig_is_compatible(trx, type, sender)) {
1231
/* The signal is not compatible with the other signals in
1237
/* Queue the signal object */
1239
if (UT_LIST_GET_LEN(trx->signals) == 0) {
1241
/* The signal list is empty: the 'sig' slot must be unused
1242
(we improve performance a bit by avoiding mem_alloc) */
1245
/* It might be that the 'sig' slot is unused also in this
1246
case, but we choose the easy way of using mem_alloc */
1248
sig = mem_alloc(sizeof(trx_sig_t));
1251
UT_LIST_ADD_LAST(signals, trx->signals, sig);
1254
sig->sender = sender;
1255
sig->receiver = receiver_thr;
1258
sig->savept = *savept;
1262
receiver_trx = thr_get_trx(receiver_thr);
1264
UT_LIST_ADD_LAST(reply_signals, receiver_trx->reply_signals,
1268
if (trx->sess->state == SESS_ERROR) {
1270
trx_sig_reply_wait_to_suspended(trx);
1273
if ((sender != TRX_SIG_SELF) || (type == TRX_SIG_BREAK_EXECUTION)) {
1277
/* If there were no other signals ahead in the queue, try to start
1278
handling of the signal */
1280
if (UT_LIST_GET_FIRST(trx->signals) == sig) {
1282
trx_sig_start_handle(trx, next_thr);
1286
/****************************************************************//**
1287
Ends signal handling. If the session is in the error state, and
1288
trx->graph_before_signal_handling != NULL, then returns control to the error
1289
handling routine of the graph (currently just returns the control to the
1290
graph root which then will send an error message to the client). */
1293
trx_end_signal_handling(
1294
/*====================*/
1295
trx_t* trx) /*!< in: trx */
1297
ut_ad(mutex_own(&kernel_mutex));
1298
ut_ad(trx->handling_signals == TRUE);
1300
trx->handling_signals = FALSE;
1302
trx->graph = trx->graph_before_signal_handling;
1304
if (trx->graph && (trx->sess->state == SESS_ERROR)) {
1306
que_fork_error_handle(trx, trx->graph);
1310
/****************************************************************//**
1311
Starts handling of a trx signal. */
1314
trx_sig_start_handle(
1315
/*=================*/
1316
trx_t* trx, /*!< in: trx handle */
1317
que_thr_t** next_thr) /*!< in/out: next query thread to run;
1318
if the value which is passed in is
1319
a pointer to a NULL pointer, then the
1320
calling function can start running
1321
a new query thread; if the parameter
1322
is NULL, it is ignored */
1327
/* We loop in this function body as long as there are queued signals
1328
we can process immediately */
1331
ut_ad(mutex_own(&kernel_mutex));
1333
if (trx->handling_signals && (UT_LIST_GET_LEN(trx->signals) == 0)) {
1335
trx_end_signal_handling(trx);
1340
if (trx->conc_state == TRX_NOT_STARTED) {
1342
trx_start_low(trx, ULINT_UNDEFINED);
1345
/* If the trx is in a lock wait state, moves the waiting query threads
1346
to the suspended state */
1348
if (trx->que_state == TRX_QUE_LOCK_WAIT) {
1350
trx_lock_wait_to_suspended(trx);
1353
/* If the session is in the error state and this trx has threads
1354
waiting for reply from signals, moves these threads to the suspended
1355
state, canceling wait reservations; note that if the transaction has
1356
sent a commit or rollback signal to itself, and its session is not in
1357
the error state, then nothing is done here. */
1359
if (trx->sess->state == SESS_ERROR) {
1360
trx_sig_reply_wait_to_suspended(trx);
1363
/* If there are no running query threads, we can start processing of a
1364
signal, otherwise we have to wait until all query threads of this
1365
transaction are aware of the arrival of the signal. */
1367
if (trx->n_active_thrs > 0) {
1372
if (trx->handling_signals == FALSE) {
1373
trx->graph_before_signal_handling = trx->graph;
1375
trx->handling_signals = TRUE;
1378
sig = UT_LIST_GET_FIRST(trx->signals);
1381
if (type == TRX_SIG_COMMIT) {
1383
trx_handle_commit_sig_off_kernel(trx, next_thr);
1385
} else if ((type == TRX_SIG_TOTAL_ROLLBACK)
1386
|| (type == TRX_SIG_ROLLBACK_TO_SAVEPT)) {
1388
trx_rollback(trx, sig, next_thr);
1390
/* No further signals can be handled until the rollback
1391
completes, therefore we return */
1395
} else if (type == TRX_SIG_ERROR_OCCURRED) {
1397
trx_rollback(trx, sig, next_thr);
1399
/* No further signals can be handled until the rollback
1400
completes, therefore we return */
1404
} else if (type == TRX_SIG_BREAK_EXECUTION) {
1406
trx_sig_reply(sig, next_thr);
1407
trx_sig_remove(trx, sig);
1415
/****************************************************************//**
1416
Send the reply message when a signal in the queue of the trx has been
1422
trx_sig_t* sig, /*!< in: signal */
1423
que_thr_t** next_thr) /*!< in/out: next query thread to run;
1424
if the value which is passed in is
1425
a pointer to a NULL pointer, then the
1426
calling function can start running
1427
a new query thread */
1429
trx_t* receiver_trx;
1432
ut_ad(mutex_own(&kernel_mutex));
1434
if (sig->receiver != NULL) {
1435
ut_ad((sig->receiver)->state == QUE_THR_SIG_REPLY_WAIT);
1437
receiver_trx = thr_get_trx(sig->receiver);
1439
UT_LIST_REMOVE(reply_signals, receiver_trx->reply_signals,
1441
ut_ad(receiver_trx->sess->state != SESS_ERROR);
1443
que_thr_end_wait(sig->receiver, next_thr);
1445
sig->receiver = NULL;
1450
/****************************************************************//**
1451
Removes a signal object from the trx signal queue. */
1456
trx_t* trx, /*!< in: trx handle */
1457
trx_sig_t* sig) /*!< in, own: signal */
1460
ut_ad(mutex_own(&kernel_mutex));
1462
ut_ad(sig->receiver == NULL);
1464
UT_LIST_REMOVE(signals, trx->signals, sig);
1465
sig->type = 0; /* reset the field to catch possible bugs */
1467
if (sig != &(trx->sig)) {
1472
/*********************************************************************//**
1473
Creates a commit command node struct.
1474
@return own: commit node struct */
1479
mem_heap_t* heap) /*!< in: mem heap where created */
1481
commit_node_t* node;
1483
node = mem_heap_alloc(heap, sizeof(commit_node_t));
1484
node->common.type = QUE_NODE_COMMIT;
1485
node->state = COMMIT_NODE_SEND;
1490
/***********************************************************//**
1491
Performs an execution step for a commit type node in a query graph.
1492
@return query thread to run next, or NULL */
1497
que_thr_t* thr) /*!< in: query thread */
1499
commit_node_t* node;
1500
que_thr_t* next_thr;
1502
node = thr->run_node;
1504
ut_ad(que_node_get_type(node) == QUE_NODE_COMMIT);
1506
if (thr->prev_node == que_node_get_parent(node)) {
1507
node->state = COMMIT_NODE_SEND;
1510
if (node->state == COMMIT_NODE_SEND) {
1511
mutex_enter(&kernel_mutex);
1513
node->state = COMMIT_NODE_WAIT;
1517
thr->state = QUE_THR_SIG_REPLY_WAIT;
1519
/* Send the commit signal to the transaction */
1521
trx_sig_send(thr_get_trx(thr), TRX_SIG_COMMIT, TRX_SIG_SELF,
1522
thr, NULL, &next_thr);
1524
mutex_exit(&kernel_mutex);
1529
ut_ad(node->state == COMMIT_NODE_WAIT);
1531
node->state = COMMIT_NODE_SEND;
1533
thr->run_node = que_node_get_parent(node);
1538
/**********************************************************************//**
1539
Does the transaction commit for MySQL.
1540
@return DB_SUCCESS or error number */
1543
trx_commit_for_mysql(
1544
/*=================*/
1545
trx_t* trx) /*!< in: trx handle */
1547
/* Because we do not do the commit by sending an Innobase
1548
sig to the transaction, we must here make sure that trx has been
1553
trx_start_if_not_started(trx);
1555
trx->op_info = "committing";
1557
mutex_enter(&kernel_mutex);
1559
trx_commit_off_kernel(trx);
1561
mutex_exit(&kernel_mutex);
1568
/**********************************************************************//**
1569
If required, flushes the log to disk if we called trx_commit_for_mysql()
1570
with trx->flush_log_later == TRUE.
1571
@return 0 or error number */
1574
trx_commit_complete_for_mysql(
1575
/*==========================*/
1576
trx_t* trx) /*!< in: trx handle */
1578
ib_uint64_t lsn = trx->commit_lsn;
1582
trx->op_info = "flushing log";
1584
if (!trx->must_flush_log_later) {
1586
} else if (srv_flush_log_at_trx_commit == 0) {
1588
} else if (srv_flush_log_at_trx_commit == 1) {
1589
if (srv_unix_file_flush_method == SRV_UNIX_NOSYNC) {
1590
/* Write the log but do not flush it to disk */
1592
log_write_up_to(lsn, LOG_WAIT_ONE_GROUP, FALSE);
1594
/* Write the log to the log files AND flush them to
1597
log_write_up_to(lsn, LOG_WAIT_ONE_GROUP, TRUE);
1599
} else if (srv_flush_log_at_trx_commit == 2) {
1601
/* Write the log but do not flush it to disk */
1603
log_write_up_to(lsn, LOG_WAIT_ONE_GROUP, FALSE);
1608
trx->must_flush_log_later = FALSE;
1615
/**********************************************************************//**
1616
Marks the latest SQL statement ended. */
1619
trx_mark_sql_stat_end(
1620
/*==================*/
1621
trx_t* trx) /*!< in: trx handle */
1625
if (trx->conc_state == TRX_NOT_STARTED) {
1629
trx->last_sql_stat_start.least_undo_no = trx->undo_no;
1632
/**********************************************************************//**
1633
Prints info about a transaction to the given file. The caller must own the
1639
FILE* f, /*!< in: output stream */
1640
trx_t* trx, /*!< in: transaction */
1641
ulint max_query_len) /*!< in: max query length to print, or 0 to
1642
use the default max length */
1646
fprintf(f, "TRANSACTION " TRX_ID_FMT, trx->id);
1648
switch (trx->conc_state) {
1649
case TRX_NOT_STARTED:
1650
fputs(", not started", f);
1653
fprintf(f, ", ACTIVE %lu sec",
1654
(ulong)difftime(time(NULL), trx->start_time));
1657
fprintf(f, ", ACTIVE (PREPARED) %lu sec",
1658
(ulong)difftime(time(NULL), trx->start_time));
1660
case TRX_COMMITTED_IN_MEMORY:
1661
fputs(", COMMITTED IN MEMORY", f);
1664
fprintf(f, " state %lu", (ulong) trx->conc_state);
1668
fprintf(f, ", process no %lu", trx->mysql_process_no);
1670
fprintf(f, ", OS thread id %lu",
1671
(ulong) os_thread_pf(trx->mysql_thread_id));
1673
if (*trx->op_info) {
1675
fputs(trx->op_info, f);
1678
if (trx->is_recovered) {
1679
fputs(" recovered trx", f);
1682
if (trx->is_purge) {
1683
fputs(" purge trx", f);
1686
if (trx->declared_to_be_inside_innodb) {
1687
fprintf(f, ", thread declared inside InnoDB %lu",
1688
(ulong) trx->n_tickets_to_enter_innodb);
1693
if (trx->mysql_n_tables_locked > 0) {
1694
fprintf(f, "mysql tables in locked %lu\n",
1695
(ulong) trx->mysql_n_tables_locked);
1700
switch (trx->que_state) {
1701
case TRX_QUE_RUNNING:
1702
newline = FALSE; break;
1703
case TRX_QUE_LOCK_WAIT:
1704
fputs("LOCK WAIT ", f); break;
1705
case TRX_QUE_ROLLING_BACK:
1706
fputs("ROLLING BACK ", f); break;
1707
case TRX_QUE_COMMITTING:
1708
fputs("COMMITTING ", f); break;
1710
fprintf(f, "que state %lu ", (ulong) trx->que_state);
1713
if (0 < UT_LIST_GET_LEN(trx->trx_locks)
1714
|| mem_heap_get_size(trx->lock_heap) > 400) {
1717
fprintf(f, "%lu lock struct(s), heap size %lu,"
1719
(ulong) UT_LIST_GET_LEN(trx->trx_locks),
1720
(ulong) mem_heap_get_size(trx->lock_heap),
1721
(ulong) lock_number_of_rows_locked(trx));
1724
if (trx->has_search_latch) {
1726
fputs(", holds adaptive hash latch", f);
1729
if (trx->undo_no != 0) {
1731
fprintf(f, ", undo log entries %llu",
1732
(ullint) trx->undo_no);
1739
if (trx->mysql_thd != NULL) {
1740
innobase_mysql_print_thd(f, trx->mysql_thd, max_query_len);
1744
/*******************************************************************//**
1745
Compares the "weight" (or size) of two transactions. Transactions that
1746
have edited non-transactional tables are considered heavier than ones
1748
@return TRUE if weight(a) >= weight(b) */
1753
const trx_t* a, /*!< in: the first transaction to be compared */
1754
const trx_t* b) /*!< in: the second transaction to be compared */
1756
ibool a_notrans_edit;
1757
ibool b_notrans_edit;
1759
/* If mysql_thd is NULL for a transaction we assume that it has
1760
not edited non-transactional tables. */
1762
a_notrans_edit = a->mysql_thd != NULL
1763
&& thd_has_edited_nontrans_tables(a->mysql_thd);
1765
b_notrans_edit = b->mysql_thd != NULL
1766
&& thd_has_edited_nontrans_tables(b->mysql_thd);
1768
if (a_notrans_edit != b_notrans_edit) {
1770
return(a_notrans_edit);
1773
/* Either both had edited non-transactional tables or both had
1774
not, we fall back to comparing the number of altered/locked
1779
"%s TRX_WEIGHT(a): %lld+%lu, TRX_WEIGHT(b): %lld+%lu\n",
1781
a->undo_no, UT_LIST_GET_LEN(a->trx_locks),
1782
b->undo_no, UT_LIST_GET_LEN(b->trx_locks));
1785
return(TRX_WEIGHT(a) >= TRX_WEIGHT(b));
1788
/****************************************************************//**
1789
Prepares a transaction. */
1792
trx_prepare_off_kernel(
1793
/*===================*/
1794
trx_t* trx) /*!< in: transaction */
1797
ib_uint64_t lsn = 0;
1800
ut_ad(mutex_own(&kernel_mutex));
1804
if (trx->insert_undo != NULL || trx->update_undo != NULL) {
1806
mutex_exit(&kernel_mutex);
1810
/* Change the undo log segment states from TRX_UNDO_ACTIVE
1811
to TRX_UNDO_PREPARED: these modifications to the file data
1812
structure define the transaction as prepared in the
1813
file-based world, at the serialization point of lsn. */
1815
mutex_enter(&(rseg->mutex));
1817
if (trx->insert_undo != NULL) {
1819
/* It is not necessary to obtain trx->undo_mutex here
1820
because only a single OS thread is allowed to do the
1821
transaction prepare for this transaction. */
1823
trx_undo_set_state_at_prepare(trx, trx->insert_undo,
1827
if (trx->update_undo) {
1828
trx_undo_set_state_at_prepare(
1829
trx, trx->update_undo, &mtr);
1832
mutex_exit(&(rseg->mutex));
1835
mtr_commit(&mtr); /* This mtr commit makes the
1836
transaction prepared in the file-based
1841
mutex_enter(&kernel_mutex);
1844
ut_ad(mutex_own(&kernel_mutex));
1846
/*--------------------------------------*/
1847
trx->conc_state = TRX_PREPARED;
1848
/*--------------------------------------*/
1851
/* Depending on the my.cnf options, we may now write the log
1852
buffer to the log files, making the prepared state of the
1853
transaction durable if the OS does not crash. We may also
1854
flush the log files to disk, making the prepared state of the
1855
transaction durable also at an OS crash or a power outage.
1857
The idea in InnoDB's group prepare is that a group of
1858
transactions gather behind a trx doing a physical disk write
1859
to log files, and when that physical write has been completed,
1860
one of those transactions does a write which prepares the whole
1861
group. Note that this group prepare will only bring benefit if
1862
there are > 2 users in the database. Then at least 2 users can
1863
gather behind one doing the physical log write to disk.
1865
TODO: find out if MySQL holds some mutex when calling this.
1866
That would spoil our group prepare algorithm. */
1868
mutex_exit(&kernel_mutex);
1870
if (srv_flush_log_at_trx_commit == 0) {
1872
} else if (srv_flush_log_at_trx_commit == 1) {
1873
if (srv_unix_file_flush_method == SRV_UNIX_NOSYNC) {
1874
/* Write the log but do not flush it to disk */
1876
log_write_up_to(lsn, LOG_WAIT_ONE_GROUP,
1879
/* Write the log to the log files AND flush
1882
log_write_up_to(lsn, LOG_WAIT_ONE_GROUP, TRUE);
1884
} else if (srv_flush_log_at_trx_commit == 2) {
1886
/* Write the log but do not flush it to disk */
1888
log_write_up_to(lsn, LOG_WAIT_ONE_GROUP, FALSE);
1893
mutex_enter(&kernel_mutex);
1897
/**********************************************************************//**
1898
Does the transaction prepare for MySQL.
1899
@return 0 or error number */
1902
trx_prepare_for_mysql(
1903
/*==================*/
1904
trx_t* trx) /*!< in: trx handle */
1906
/* Because we do not do the prepare by sending an Innobase
1907
sig to the transaction, we must here make sure that trx has been
1912
trx->op_info = "preparing";
1914
trx_start_if_not_started(trx);
1916
mutex_enter(&kernel_mutex);
1918
trx_prepare_off_kernel(trx);
1920
mutex_exit(&kernel_mutex);
1927
/**********************************************************************//**
1928
This function is used to find number of prepared transactions and
1929
their transaction objects for a recovery.
1930
@return number of prepared transactions stored in xid_list */
1933
trx_recover_for_mysql(
1934
/*==================*/
1935
XID* xid_list, /*!< in/out: prepared transactions */
1936
ulint len) /*!< in: number of slots in xid_list */
1944
/* We should set those transactions which are in the prepared state
1947
mutex_enter(&kernel_mutex);
1949
trx = UT_LIST_GET_FIRST(trx_sys->trx_list);
1952
if (trx->conc_state == TRX_PREPARED) {
1953
xid_list[count] = trx->xid;
1956
ut_print_timestamp(stderr);
1958
" InnoDB: Starting recovery for"
1959
" XA transactions...\n");
1962
ut_print_timestamp(stderr);
1964
" InnoDB: Transaction " TRX_ID_FMT " in"
1965
" prepared state after recovery\n",
1968
ut_print_timestamp(stderr);
1970
" InnoDB: Transaction contains changes"
1972
(ullint) trx->undo_no);
1981
trx = UT_LIST_GET_NEXT(trx_list, trx);
1984
mutex_exit(&kernel_mutex);
1987
ut_print_timestamp(stderr);
1989
" InnoDB: %lu transactions in prepared state"
1990
" after recovery\n",
1994
return ((int) count);
1997
/*******************************************************************//**
1998
This function is used to find one X/Open XA distributed transaction
1999
which is in the prepared state
2000
@return trx or NULL */
2005
XID* xid) /*!< in: X/Open XA transaction identification */
2014
mutex_enter(&kernel_mutex);
2016
trx = UT_LIST_GET_FIRST(trx_sys->trx_list);
2019
/* Compare two X/Open XA transaction id's: their
2020
length should be the same and binary comparison
2021
of gtrid_length+bqual_length bytes should be
2024
if (xid->gtrid_length == trx->xid.gtrid_length
2025
&& xid->bqual_length == trx->xid.bqual_length
2026
&& memcmp(xid->data, trx->xid.data,
2027
xid->gtrid_length + xid->bqual_length) == 0) {
2031
trx = UT_LIST_GET_NEXT(trx_list, trx);
2034
mutex_exit(&kernel_mutex);
2037
if (trx->conc_state != TRX_PREPARED) {