1
/******************************************************
6
Created 3/26/1996 Heikki Tuuri
7
*******************************************************/
19
#include "lock0lock.h"
22
#include "read0read.h"
28
#include "ha_prototypes.h"
30
/* Copy of the prototype for innobase_mysql_print_thd: this
31
copy MUST be equal to the one in mysql/sql/ha_innodb.cc ! */
33
void innobase_mysql_print_thd(
38
/* Dummy session used currently in MySQL interface */
39
sess_t* trx_dummy_sess = NULL;
41
/* Number of transactions currently allocated for MySQL: protected by
43
ulint trx_n_mysql_transactions = 0;
45
/*****************************************************************
46
Starts the transaction if it is not yet started. */
49
trx_start_if_not_started_noninline(
50
/*===============================*/
51
trx_t* trx) /* in: transaction */
53
trx_start_if_not_started(trx);
56
/*****************************************************************
57
Set detailed error message for the transaction. */
60
trx_set_detailed_error(
61
/*===================*/
62
trx_t* trx, /* in: transaction struct */
63
const char* msg) /* in: detailed error message */
65
ut_strlcpy(trx->detailed_error, msg, sizeof(trx->detailed_error));
68
/*****************************************************************
69
Set detailed error message for the transaction from a file. Note that the
70
file is rewinded before reading from it. */
73
trx_set_detailed_error_from_file(
74
/*=============================*/
75
trx_t* trx, /* in: transaction struct */
76
FILE* file) /* in: file to read message from */
78
os_file_read_string(file, trx->detailed_error,
79
sizeof(trx->detailed_error));
82
/********************************************************************
83
Retrieves the error_info field from a trx. */
88
/* out: the error info */
89
trx_t* trx) /* in: trx object */
91
return(trx->error_info);
94
/********************************************************************
95
Creates and initializes a transaction object. */
100
/* out, own: the transaction */
101
sess_t* sess) /* in: session or NULL */
105
ut_ad(mutex_own(&kernel_mutex));
107
trx = mem_alloc(sizeof(trx_t));
109
trx->magic_n = TRX_MAGIC_N;
114
trx->conc_state = TRX_NOT_STARTED;
115
trx->start_time = time(NULL);
117
trx->isolation_level = TRX_ISO_REPEATABLE_READ;
119
trx->id = ut_dulint_zero;
120
trx->no = ut_dulint_max;
122
trx->support_xa = TRUE;
124
trx->check_foreigns = TRUE;
125
trx->check_unique_secondary = TRUE;
127
trx->flush_log_later = FALSE;
128
trx->must_flush_log_later = FALSE;
130
trx->dict_operation = FALSE;
132
trx->mysql_thd = NULL;
133
trx->mysql_query_str = NULL;
134
trx->active_trans = 0;
137
trx->n_mysql_tables_in_use = 0;
138
trx->mysql_n_tables_locked = 0;
140
trx->mysql_log_file_name = NULL;
141
trx->mysql_log_offset = 0;
143
mutex_create(&trx->undo_mutex, SYNC_TRX_UNDO);
147
trx->undo_no = ut_dulint_zero;
148
trx->last_sql_stat_start.least_undo_no = ut_dulint_zero;
149
trx->insert_undo = NULL;
150
trx->update_undo = NULL;
151
trx->undo_no_arr = NULL;
153
trx->error_state = DB_SUCCESS;
154
trx->detailed_error[0] = '\0';
157
trx->que_state = TRX_QUE_RUNNING;
158
trx->n_active_thrs = 0;
160
trx->handling_signals = FALSE;
162
UT_LIST_INIT(trx->signals);
163
UT_LIST_INIT(trx->reply_signals);
167
trx->wait_lock = NULL;
168
trx->was_chosen_as_deadlock_victim = FALSE;
169
UT_LIST_INIT(trx->wait_thrs);
171
trx->lock_heap = mem_heap_create_in_buffer(256);
172
UT_LIST_INIT(trx->trx_locks);
174
UT_LIST_INIT(trx->trx_savepoints);
176
trx->dict_operation_lock_mode = 0;
177
trx->has_search_latch = FALSE;
178
trx->search_latch_timeout = BTR_SEA_TIMEOUT;
180
trx->declared_to_be_inside_innodb = FALSE;
181
trx->n_tickets_to_enter_innodb = 0;
183
trx->auto_inc_lock = NULL;
185
trx->global_read_view_heap = mem_heap_create(256);
186
trx->global_read_view = NULL;
187
trx->read_view = NULL;
189
/* Set X/Open XA transaction identification to NULL */
190
memset(&trx->xid, 0, sizeof(trx->xid));
191
trx->xid.formatID = -1;
193
trx->n_autoinc_rows = 0;
195
trx_reset_new_rec_lock_info(trx);
200
/************************************************************************
201
Creates a transaction object for MySQL. */
204
trx_allocate_for_mysql(void)
205
/*========================*/
206
/* out, own: transaction object */
210
mutex_enter(&kernel_mutex);
212
/* Open a dummy session */
214
if (!trx_dummy_sess) {
215
trx_dummy_sess = sess_open();
218
trx = trx_create(trx_dummy_sess);
220
trx_n_mysql_transactions++;
222
UT_LIST_ADD_FIRST(mysql_trx_list, trx_sys->mysql_trx_list, trx);
224
mutex_exit(&kernel_mutex);
226
trx->mysql_thread_id = os_thread_get_curr_id();
228
trx->mysql_process_no = os_proc_get_number();
233
/************************************************************************
234
Creates a transaction object for background operations by the master thread. */
237
trx_allocate_for_background(void)
238
/*=============================*/
239
/* out, own: transaction object */
243
mutex_enter(&kernel_mutex);
245
/* Open a dummy session */
247
if (!trx_dummy_sess) {
248
trx_dummy_sess = sess_open();
251
trx = trx_create(trx_dummy_sess);
253
mutex_exit(&kernel_mutex);
258
/************************************************************************
259
Releases the search latch if trx has reserved it. */
262
trx_search_latch_release_if_reserved(
263
/*=================================*/
264
trx_t* trx) /* in: transaction */
266
if (trx->has_search_latch) {
267
rw_lock_s_unlock(&btr_search_latch);
269
trx->has_search_latch = FALSE;
273
/************************************************************************
274
Frees a transaction object. */
279
trx_t* trx) /* in, own: trx object */
281
ut_ad(mutex_own(&kernel_mutex));
283
if (trx->declared_to_be_inside_innodb) {
284
ut_print_timestamp(stderr);
285
fputs(" InnoDB: Error: Freeing a trx which is declared"
286
" to be processing\n"
287
"InnoDB: inside InnoDB.\n", stderr);
288
trx_print(stderr, trx, 600);
292
if (trx->n_mysql_tables_in_use != 0
293
|| trx->mysql_n_tables_locked != 0) {
295
ut_print_timestamp(stderr);
297
" InnoDB: Error: MySQL is freeing a thd\n"
298
"InnoDB: though trx->n_mysql_tables_in_use is %lu\n"
299
"InnoDB: and trx->mysql_n_tables_locked is %lu.\n",
300
(ulong)trx->n_mysql_tables_in_use,
301
(ulong)trx->mysql_n_tables_locked);
303
trx_print(stderr, trx, 600);
305
ut_print_buf(stderr, trx, sizeof(trx_t));
308
ut_a(trx->magic_n == TRX_MAGIC_N);
310
trx->magic_n = 11112222;
312
ut_a(trx->conc_state == TRX_NOT_STARTED);
314
mutex_free(&(trx->undo_mutex));
316
ut_a(trx->insert_undo == NULL);
317
ut_a(trx->update_undo == NULL);
319
if (trx->undo_no_arr) {
320
trx_undo_arr_free(trx->undo_no_arr);
323
ut_a(UT_LIST_GET_LEN(trx->signals) == 0);
324
ut_a(UT_LIST_GET_LEN(trx->reply_signals) == 0);
326
ut_a(trx->wait_lock == NULL);
327
ut_a(UT_LIST_GET_LEN(trx->wait_thrs) == 0);
329
ut_a(!trx->has_search_latch);
330
ut_a(!trx->auto_inc_lock);
332
ut_a(trx->dict_operation_lock_mode == 0);
334
if (trx->lock_heap) {
335
mem_heap_free(trx->lock_heap);
338
ut_a(UT_LIST_GET_LEN(trx->trx_locks) == 0);
340
if (trx->global_read_view_heap) {
341
mem_heap_free(trx->global_read_view_heap);
344
trx->global_read_view = NULL;
346
ut_a(trx->read_view == NULL);
351
/************************************************************************
352
Frees a transaction object for MySQL. */
357
trx_t* trx) /* in, own: trx object */
359
mutex_enter(&kernel_mutex);
361
UT_LIST_REMOVE(mysql_trx_list, trx_sys->mysql_trx_list, trx);
365
ut_a(trx_n_mysql_transactions > 0);
367
trx_n_mysql_transactions--;
369
mutex_exit(&kernel_mutex);
372
/************************************************************************
373
Frees a transaction object of a background operation of the master thread. */
376
trx_free_for_background(
377
/*====================*/
378
trx_t* trx) /* in, own: trx object */
380
mutex_enter(&kernel_mutex);
384
mutex_exit(&kernel_mutex);
387
/********************************************************************
388
Inserts the trx handle in the trx system trx list in the right position.
389
The list is sorted on the trx id so that the biggest id is at the list
390
start. This function is used at the database startup to insert incomplete
391
transactions to the list. */
394
trx_list_insert_ordered(
395
/*====================*/
396
trx_t* trx) /* in: trx handle */
400
ut_ad(mutex_own(&kernel_mutex));
402
trx2 = UT_LIST_GET_FIRST(trx_sys->trx_list);
404
while (trx2 != NULL) {
405
if (ut_dulint_cmp(trx->id, trx2->id) >= 0) {
407
ut_ad(ut_dulint_cmp(trx->id, trx2->id) == 1);
410
trx2 = UT_LIST_GET_NEXT(trx_list, trx2);
414
trx2 = UT_LIST_GET_PREV(trx_list, trx2);
417
UT_LIST_ADD_FIRST(trx_list, trx_sys->trx_list, trx);
419
UT_LIST_INSERT_AFTER(trx_list, trx_sys->trx_list,
423
UT_LIST_ADD_LAST(trx_list, trx_sys->trx_list, trx);
427
/********************************************************************
428
Creates trx objects for transactions and initializes the trx list of
429
trx_sys at database start. Rollback segment and undo log lists must
430
already exist when this function is called, because the lists of
431
transactions to be rolled back or cleaned up are built based on the
435
trx_lists_init_at_db_start(void)
436
/*============================*/
442
UT_LIST_INIT(trx_sys->trx_list);
444
/* Look from the rollback segments if there exist undo logs for
447
rseg = UT_LIST_GET_FIRST(trx_sys->rseg_list);
449
while (rseg != NULL) {
450
undo = UT_LIST_GET_FIRST(rseg->insert_undo_list);
452
while (undo != NULL) {
454
trx = trx_create(NULL);
456
trx->id = undo->trx_id;
457
trx->xid = undo->xid;
458
trx->insert_undo = undo;
461
if (undo->state != TRX_UNDO_ACTIVE) {
463
/* Prepared transactions are left in
464
the prepared state waiting for a
465
commit or abort decision from MySQL */
467
if (undo->state == TRX_UNDO_PREPARED) {
470
"InnoDB: Transaction %lu %lu"
472
" XA prepared state.\n",
473
ut_dulint_get_high(trx->id),
474
ut_dulint_get_low(trx->id));
476
if (srv_force_recovery == 0) {
478
trx->conc_state = TRX_PREPARED;
482
" innodb_force_recovery"
487
trx->conc_state = TRX_ACTIVE;
491
= TRX_COMMITTED_IN_MEMORY;
494
/* We give a dummy value for the trx no;
495
this should have no relevance since purge
496
is not interested in committed transaction
497
numbers, unless they are in the history
498
list, in which case it looks the number
499
from the disk based undo log structure */
503
trx->conc_state = TRX_ACTIVE;
505
/* A running transaction always has the number
506
field inited to ut_dulint_max */
508
trx->no = ut_dulint_max;
511
if (undo->dict_operation) {
512
trx->dict_operation = undo->dict_operation;
513
trx->table_id = undo->table_id;
517
trx->undo_no = ut_dulint_add(undo->top_undo_no,
521
trx_list_insert_ordered(trx);
523
undo = UT_LIST_GET_NEXT(undo_list, undo);
526
undo = UT_LIST_GET_FIRST(rseg->update_undo_list);
528
while (undo != NULL) {
529
trx = trx_get_on_id(undo->trx_id);
532
trx = trx_create(NULL);
534
trx->id = undo->trx_id;
535
trx->xid = undo->xid;
537
if (undo->state != TRX_UNDO_ACTIVE) {
539
/* Prepared transactions are left in
540
the prepared state waiting for a
541
commit or abort decision from MySQL */
543
if (undo->state == TRX_UNDO_PREPARED) {
545
"InnoDB: Transaction"
546
" %lu %lu was in the"
547
" XA prepared state.\n",
553
if (srv_force_recovery == 0) {
560
" innodb_force_recovery"
570
= TRX_COMMITTED_IN_MEMORY;
573
/* We give a dummy value for the trx
578
trx->conc_state = TRX_ACTIVE;
580
/* A running transaction always has
581
the number field inited to
584
trx->no = ut_dulint_max;
588
trx_list_insert_ordered(trx);
590
if (undo->dict_operation) {
592
= undo->dict_operation;
593
trx->table_id = undo->table_id;
597
trx->update_undo = undo;
600
&& (ut_dulint_cmp(undo->top_undo_no,
601
trx->undo_no) >= 0)) {
603
trx->undo_no = ut_dulint_add(undo->top_undo_no,
607
undo = UT_LIST_GET_NEXT(undo_list, undo);
610
rseg = UT_LIST_GET_NEXT(rseg_list, rseg);
614
/**********************************************************************
615
Assigns a rollback segment to a transaction in a round-robin fashion.
616
Skips the SYSTEM rollback segment if another is available. */
619
trx_assign_rseg(void)
620
/*=================*/
621
/* out: assigned rollback segment id */
623
trx_rseg_t* rseg = trx_sys->latest_rseg;
625
ut_ad(mutex_own(&kernel_mutex));
627
/* Get next rseg in a round-robin fashion */
629
rseg = UT_LIST_GET_NEXT(rseg_list, rseg);
632
rseg = UT_LIST_GET_FIRST(trx_sys->rseg_list);
635
/* If it is the SYSTEM rollback segment, and there exist others, skip
638
if ((rseg->id == TRX_SYS_SYSTEM_RSEG_ID)
639
&& (UT_LIST_GET_LEN(trx_sys->rseg_list) > 1)) {
643
trx_sys->latest_rseg = rseg;
648
/********************************************************************
649
Starts a new transaction. */
655
trx_t* trx, /* in: transaction */
656
ulint rseg_id)/* in: rollback segment id; if ULINT_UNDEFINED
657
is passed, the system chooses the rollback segment
658
automatically in a round-robin fashion */
662
ut_ad(mutex_own(&kernel_mutex));
663
ut_ad(trx->rseg == NULL);
666
trx->id = ut_dulint_zero;
667
trx->conc_state = TRX_ACTIVE;
668
trx->start_time = time(NULL);
673
ut_ad(trx->conc_state != TRX_ACTIVE);
675
if (rseg_id == ULINT_UNDEFINED) {
677
rseg_id = trx_assign_rseg();
680
rseg = trx_sys_get_nth_rseg(trx_sys, rseg_id);
682
trx->id = trx_sys_get_new_trx_id();
684
/* The initial value for trx->no: ut_dulint_max is used in
685
read_view_open_now: */
687
trx->no = ut_dulint_max;
691
trx->conc_state = TRX_ACTIVE;
692
trx->start_time = time(NULL);
694
UT_LIST_ADD_FIRST(trx_list, trx_sys->trx_list, trx);
699
/********************************************************************
700
Starts a new transaction. */
706
trx_t* trx, /* in: transaction */
707
ulint rseg_id)/* in: rollback segment id; if ULINT_UNDEFINED
708
is passed, the system chooses the rollback segment
709
automatically in a round-robin fashion */
713
mutex_enter(&kernel_mutex);
715
ret = trx_start_low(trx, rseg_id);
717
mutex_exit(&kernel_mutex);
722
/********************************************************************
723
Commits a transaction. */
726
trx_commit_off_kernel(
727
/*==================*/
728
trx_t* trx) /* in: transaction */
730
page_t* update_hdr_page;
734
ibool must_flush_log = FALSE;
737
ut_ad(mutex_own(&kernel_mutex));
739
trx->must_flush_log_later = FALSE;
743
if (trx->insert_undo != NULL || trx->update_undo != NULL) {
745
mutex_exit(&kernel_mutex);
749
must_flush_log = TRUE;
751
/* Change the undo log segment states from TRX_UNDO_ACTIVE
752
to some other state: these modifications to the file data
753
structure define the transaction as committed in the file
754
based world, at the serialization point of the log sequence
755
number lsn obtained below. */
757
mutex_enter(&(rseg->mutex));
759
if (trx->insert_undo != NULL) {
760
trx_undo_set_state_at_finish(
761
rseg, trx, trx->insert_undo, &mtr);
764
undo = trx->update_undo;
767
mutex_enter(&kernel_mutex);
768
trx->no = trx_sys_get_new_trx_no();
770
mutex_exit(&kernel_mutex);
772
/* It is not necessary to obtain trx->undo_mutex here
773
because only a single OS thread is allowed to do the
774
transaction commit for this transaction. */
776
update_hdr_page = trx_undo_set_state_at_finish(
777
rseg, trx, undo, &mtr);
779
/* We have to do the cleanup for the update log while
780
holding the rseg mutex because update log headers
781
have to be put to the history list in the order of
784
trx_undo_update_cleanup(trx, update_hdr_page, &mtr);
787
mutex_exit(&(rseg->mutex));
789
/* Update the latest MySQL binlog name and offset info
790
in trx sys header if MySQL binlogging is on or the database
791
server is a MySQL replication slave */
793
if (trx->mysql_log_file_name
794
&& trx->mysql_log_file_name[0] != '\0') {
795
trx_sys_update_mysql_binlog_offset(
796
trx->mysql_log_file_name,
797
trx->mysql_log_offset,
798
TRX_SYS_MYSQL_LOG_INFO, &mtr);
799
trx->mysql_log_file_name = NULL;
802
/* The following call commits the mini-transaction, making the
803
whole transaction committed in the file-based world, at this
804
log sequence number. The transaction becomes 'durable' when
805
we write the log to disk, but in the logical sense the commit
806
in the file-based data structures (undo logs etc.) happens
809
NOTE that transaction numbers, which are assigned only to
810
transactions with an update undo log, do not necessarily come
811
in exactly the same order as commit lsn's, if the transactions
812
have different rollback segments. To get exactly the same
813
order we should hold the kernel mutex up to this point,
814
adding to to the contention of the kernel mutex. However, if
815
a transaction T2 is able to see modifications made by
816
a transaction T1, T2 will always get a bigger transaction
817
number and a bigger commit lsn than T1. */
824
mutex_enter(&kernel_mutex);
827
ut_ad(trx->conc_state == TRX_ACTIVE
828
|| trx->conc_state == TRX_PREPARED);
829
ut_ad(mutex_own(&kernel_mutex));
831
/* The following assignment makes the transaction committed in memory
832
and makes its changes to data visible to other transactions.
833
NOTE that there is a small discrepancy from the strict formal
834
visibility rules here: a human user of the database can see
835
modifications made by another transaction T even before the necessary
836
log segment has been flushed to the disk. If the database happens to
837
crash before the flush, the user has seen modifications from T which
838
will never be a committed transaction. However, any transaction T2
839
which sees the modifications of the committing transaction T, and
840
which also itself makes modifications to the database, will get an lsn
841
larger than the committing transaction T. In the case where the log
842
flush fails, and T never gets committed, also T2 will never get
845
/*--------------------------------------*/
846
trx->conc_state = TRX_COMMITTED_IN_MEMORY;
847
/*--------------------------------------*/
849
lock_release_off_kernel(trx);
851
if (trx->global_read_view) {
852
read_view_close(trx->global_read_view);
853
mem_heap_empty(trx->global_read_view_heap);
854
trx->global_read_view = NULL;
857
trx->read_view = NULL;
859
if (must_flush_log) {
861
mutex_exit(&kernel_mutex);
863
if (trx->insert_undo != NULL) {
865
trx_undo_insert_cleanup(trx);
868
/* NOTE that we could possibly make a group commit more
869
efficient here: call os_thread_yield here to allow also other
870
trxs to come to commit! */
872
/*-------------------------------------*/
874
/* Depending on the my.cnf options, we may now write the log
875
buffer to the log files, making the transaction durable if
876
the OS does not crash. We may also flush the log files to
877
disk, making the transaction durable also at an OS crash or a
880
The idea in InnoDB's group commit is that a group of
881
transactions gather behind a trx doing a physical disk write
882
to log files, and when that physical write has been completed,
883
one of those transactions does a write which commits the whole
884
group. Note that this group commit will only bring benefit if
885
there are > 2 users in the database. Then at least 2 users can
886
gather behind one doing the physical log write to disk.
888
If we are calling trx_commit() under MySQL's binlog mutex, we
889
will delay possible log write and flush to a separate function
890
trx_commit_complete_for_mysql(), which is only called when the
891
thread has released the binlog mutex. This is to make the
892
group commit algorithm to work. Otherwise, the MySQL binlog
893
mutex would serialize all commits and prevent a group of
894
transactions from gathering. */
896
if (trx->flush_log_later) {
898
trx->must_flush_log_later = TRUE;
899
} else if (srv_flush_log_at_trx_commit == 0) {
901
} else if (srv_flush_log_at_trx_commit == 1) {
902
if (srv_unix_file_flush_method == SRV_UNIX_NOSYNC) {
903
/* Write the log but do not flush it to disk */
905
log_write_up_to(lsn, LOG_WAIT_ONE_GROUP,
908
/* Write the log to the log files AND flush
911
log_write_up_to(lsn, LOG_WAIT_ONE_GROUP, TRUE);
913
} else if (srv_flush_log_at_trx_commit == 2) {
915
/* Write the log but do not flush it to disk */
917
log_write_up_to(lsn, LOG_WAIT_ONE_GROUP, FALSE);
922
trx->commit_lsn = lsn;
924
/*-------------------------------------*/
926
mutex_enter(&kernel_mutex);
929
/* Free savepoints */
930
trx_roll_savepoints_free(trx, NULL);
932
trx->conc_state = TRX_NOT_STARTED;
934
trx->undo_no = ut_dulint_zero;
935
trx->last_sql_stat_start.least_undo_no = ut_dulint_zero;
937
ut_ad(UT_LIST_GET_LEN(trx->wait_thrs) == 0);
938
ut_ad(UT_LIST_GET_LEN(trx->trx_locks) == 0);
940
UT_LIST_REMOVE(trx_list, trx_sys->trx_list, trx);
943
/********************************************************************
944
Cleans up a transaction at database startup. The cleanup is needed if
945
the transaction already got to the middle of a commit when the database
946
crashed, andf we cannot roll it back. */
949
trx_cleanup_at_db_startup(
950
/*======================*/
951
trx_t* trx) /* in: transaction */
953
if (trx->insert_undo != NULL) {
955
trx_undo_insert_cleanup(trx);
958
trx->conc_state = TRX_NOT_STARTED;
960
trx->undo_no = ut_dulint_zero;
961
trx->last_sql_stat_start.least_undo_no = ut_dulint_zero;
963
UT_LIST_REMOVE(trx_list, trx_sys->trx_list, trx);
966
/************************************************************************
967
Assigns a read view for a consistent read query. All the consistent reads
968
within the same transaction will get the same read view, which is created
969
when this function is first called for a new started transaction. */
972
trx_assign_read_view(
973
/*=================*/
974
/* out: consistent read view */
975
trx_t* trx) /* in: active transaction */
977
ut_ad(trx->conc_state == TRX_ACTIVE);
979
if (trx->read_view) {
980
return(trx->read_view);
983
mutex_enter(&kernel_mutex);
985
if (!trx->read_view) {
986
trx->read_view = read_view_open_now(
987
trx->id, trx->global_read_view_heap);
988
trx->global_read_view = trx->read_view;
991
mutex_exit(&kernel_mutex);
993
return(trx->read_view);
996
/********************************************************************
997
Commits a transaction. NOTE that the kernel mutex is temporarily released. */
1000
trx_handle_commit_sig_off_kernel(
1001
/*=============================*/
1002
trx_t* trx, /* in: transaction */
1003
que_thr_t** next_thr) /* in/out: next query thread to run;
1004
if the value which is passed in is
1005
a pointer to a NULL pointer, then the
1006
calling function can start running
1007
a new query thread */
1010
trx_sig_t* next_sig;
1012
ut_ad(mutex_own(&kernel_mutex));
1014
trx->que_state = TRX_QUE_COMMITTING;
1016
trx_commit_off_kernel(trx);
1018
ut_ad(UT_LIST_GET_LEN(trx->wait_thrs) == 0);
1020
/* Remove all TRX_SIG_COMMIT signals from the signal queue and send
1021
reply messages to them */
1023
sig = UT_LIST_GET_FIRST(trx->signals);
1025
while (sig != NULL) {
1026
next_sig = UT_LIST_GET_NEXT(signals, sig);
1028
if (sig->type == TRX_SIG_COMMIT) {
1030
trx_sig_reply(sig, next_thr);
1031
trx_sig_remove(trx, sig);
1037
trx->que_state = TRX_QUE_RUNNING;
1040
/***************************************************************
1041
The transaction must be in the TRX_QUE_LOCK_WAIT state. Puts it to
1042
the TRX_QUE_RUNNING state and releases query threads which were
1043
waiting for a lock in the wait_thrs list. */
1048
trx_t* trx) /* in: transaction */
1052
ut_ad(mutex_own(&kernel_mutex));
1053
ut_ad(trx->que_state == TRX_QUE_LOCK_WAIT);
1055
thr = UT_LIST_GET_FIRST(trx->wait_thrs);
1057
while (thr != NULL) {
1058
que_thr_end_wait_no_next_thr(thr);
1060
UT_LIST_REMOVE(trx_thrs, trx->wait_thrs, thr);
1062
thr = UT_LIST_GET_FIRST(trx->wait_thrs);
1065
trx->que_state = TRX_QUE_RUNNING;
1068
/***************************************************************
1069
Moves the query threads in the lock wait list to the SUSPENDED state and puts
1070
the transaction to the TRX_QUE_RUNNING state. */
1073
trx_lock_wait_to_suspended(
1074
/*=======================*/
1075
trx_t* trx) /* in: transaction in the TRX_QUE_LOCK_WAIT state */
1079
ut_ad(mutex_own(&kernel_mutex));
1080
ut_ad(trx->que_state == TRX_QUE_LOCK_WAIT);
1082
thr = UT_LIST_GET_FIRST(trx->wait_thrs);
1084
while (thr != NULL) {
1085
thr->state = QUE_THR_SUSPENDED;
1087
UT_LIST_REMOVE(trx_thrs, trx->wait_thrs, thr);
1089
thr = UT_LIST_GET_FIRST(trx->wait_thrs);
1092
trx->que_state = TRX_QUE_RUNNING;
1095
/***************************************************************
1096
Moves the query threads in the sig reply wait list of trx to the SUSPENDED
1100
trx_sig_reply_wait_to_suspended(
1101
/*============================*/
1102
trx_t* trx) /* in: transaction */
1107
ut_ad(mutex_own(&kernel_mutex));
1109
sig = UT_LIST_GET_FIRST(trx->reply_signals);
1111
while (sig != NULL) {
1112
thr = sig->receiver;
1114
ut_ad(thr->state == QUE_THR_SIG_REPLY_WAIT);
1116
thr->state = QUE_THR_SUSPENDED;
1118
sig->receiver = NULL;
1120
UT_LIST_REMOVE(reply_signals, trx->reply_signals, sig);
1122
sig = UT_LIST_GET_FIRST(trx->reply_signals);
1126
/*********************************************************************
1127
Checks the compatibility of a new signal with the other signals in the
1131
trx_sig_is_compatible(
1132
/*==================*/
1133
/* out: TRUE if the signal can be queued */
1134
trx_t* trx, /* in: trx handle */
1135
ulint type, /* in: signal type */
1136
ulint sender) /* in: TRX_SIG_SELF or TRX_SIG_OTHER_SESS */
1140
ut_ad(mutex_own(&kernel_mutex));
1142
if (UT_LIST_GET_LEN(trx->signals) == 0) {
1147
if (sender == TRX_SIG_SELF) {
1148
if (type == TRX_SIG_ERROR_OCCURRED) {
1152
} else if (type == TRX_SIG_BREAK_EXECUTION) {
1160
ut_ad(sender == TRX_SIG_OTHER_SESS);
1162
sig = UT_LIST_GET_FIRST(trx->signals);
1164
if (type == TRX_SIG_COMMIT) {
1165
while (sig != NULL) {
1167
if (sig->type == TRX_SIG_TOTAL_ROLLBACK) {
1172
sig = UT_LIST_GET_NEXT(signals, sig);
1177
} else if (type == TRX_SIG_TOTAL_ROLLBACK) {
1178
while (sig != NULL) {
1180
if (sig->type == TRX_SIG_COMMIT) {
1185
sig = UT_LIST_GET_NEXT(signals, sig);
1190
} else if (type == TRX_SIG_BREAK_EXECUTION) {
1200
/********************************************************************
1201
Sends a signal to a trx object. */
1206
trx_t* trx, /* in: trx handle */
1207
ulint type, /* in: signal type */
1208
ulint sender, /* in: TRX_SIG_SELF or
1209
TRX_SIG_OTHER_SESS */
1210
que_thr_t* receiver_thr, /* in: query thread which wants the
1211
reply, or NULL; if type is
1212
TRX_SIG_END_WAIT, this must be NULL */
1213
trx_savept_t* savept, /* in: possible rollback savepoint, or
1215
que_thr_t** next_thr) /* in/out: next query thread to run;
1216
if the value which is passed in is
1217
a pointer to a NULL pointer, then the
1218
calling function can start running
1219
a new query thread; if the parameter
1220
is NULL, it is ignored */
1223
trx_t* receiver_trx;
1226
ut_ad(mutex_own(&kernel_mutex));
1228
if (!trx_sig_is_compatible(trx, type, sender)) {
1229
/* The signal is not compatible with the other signals in
1235
/* Queue the signal object */
1237
if (UT_LIST_GET_LEN(trx->signals) == 0) {
1239
/* The signal list is empty: the 'sig' slot must be unused
1240
(we improve performance a bit by avoiding mem_alloc) */
1243
/* It might be that the 'sig' slot is unused also in this
1244
case, but we choose the easy way of using mem_alloc */
1246
sig = mem_alloc(sizeof(trx_sig_t));
1249
UT_LIST_ADD_LAST(signals, trx->signals, sig);
1252
sig->sender = sender;
1253
sig->receiver = receiver_thr;
1256
sig->savept = *savept;
1260
receiver_trx = thr_get_trx(receiver_thr);
1262
UT_LIST_ADD_LAST(reply_signals, receiver_trx->reply_signals,
1266
if (trx->sess->state == SESS_ERROR) {
1268
trx_sig_reply_wait_to_suspended(trx);
1271
if ((sender != TRX_SIG_SELF) || (type == TRX_SIG_BREAK_EXECUTION)) {
1275
/* If there were no other signals ahead in the queue, try to start
1276
handling of the signal */
1278
if (UT_LIST_GET_FIRST(trx->signals) == sig) {
1280
trx_sig_start_handle(trx, next_thr);
1284
/********************************************************************
1285
Ends signal handling. If the session is in the error state, and
1286
trx->graph_before_signal_handling != NULL, then returns control to the error
1287
handling routine of the graph (currently just returns the control to the
1288
graph root which then will send an error message to the client). */
1291
trx_end_signal_handling(
1292
/*====================*/
1293
trx_t* trx) /* in: trx */
1295
ut_ad(mutex_own(&kernel_mutex));
1296
ut_ad(trx->handling_signals == TRUE);
1298
trx->handling_signals = FALSE;
1300
trx->graph = trx->graph_before_signal_handling;
1302
if (trx->graph && (trx->sess->state == SESS_ERROR)) {
1304
que_fork_error_handle(trx, trx->graph);
1308
/********************************************************************
1309
Starts handling of a trx signal. */
1312
trx_sig_start_handle(
1313
/*=================*/
1314
trx_t* trx, /* in: trx handle */
1315
que_thr_t** next_thr) /* in/out: next query thread to run;
1316
if the value which is passed in is
1317
a pointer to a NULL pointer, then the
1318
calling function can start running
1319
a new query thread; if the parameter
1320
is NULL, it is ignored */
1325
/* We loop in this function body as long as there are queued signals
1326
we can process immediately */
1329
ut_ad(mutex_own(&kernel_mutex));
1331
if (trx->handling_signals && (UT_LIST_GET_LEN(trx->signals) == 0)) {
1333
trx_end_signal_handling(trx);
1338
if (trx->conc_state == TRX_NOT_STARTED) {
1340
trx_start_low(trx, ULINT_UNDEFINED);
1343
/* If the trx is in a lock wait state, moves the waiting query threads
1344
to the suspended state */
1346
if (trx->que_state == TRX_QUE_LOCK_WAIT) {
1348
trx_lock_wait_to_suspended(trx);
1351
/* If the session is in the error state and this trx has threads
1352
waiting for reply from signals, moves these threads to the suspended
1353
state, canceling wait reservations; note that if the transaction has
1354
sent a commit or rollback signal to itself, and its session is not in
1355
the error state, then nothing is done here. */
1357
if (trx->sess->state == SESS_ERROR) {
1358
trx_sig_reply_wait_to_suspended(trx);
1361
/* If there are no running query threads, we can start processing of a
1362
signal, otherwise we have to wait until all query threads of this
1363
transaction are aware of the arrival of the signal. */
1365
if (trx->n_active_thrs > 0) {
1370
if (trx->handling_signals == FALSE) {
1371
trx->graph_before_signal_handling = trx->graph;
1373
trx->handling_signals = TRUE;
1376
sig = UT_LIST_GET_FIRST(trx->signals);
1379
if (type == TRX_SIG_COMMIT) {
1381
trx_handle_commit_sig_off_kernel(trx, next_thr);
1383
} else if ((type == TRX_SIG_TOTAL_ROLLBACK)
1384
|| (type == TRX_SIG_ROLLBACK_TO_SAVEPT)) {
1386
trx_rollback(trx, sig, next_thr);
1388
/* No further signals can be handled until the rollback
1389
completes, therefore we return */
1393
} else if (type == TRX_SIG_ERROR_OCCURRED) {
1395
trx_rollback(trx, sig, next_thr);
1397
/* No further signals can be handled until the rollback
1398
completes, therefore we return */
1402
} else if (type == TRX_SIG_BREAK_EXECUTION) {
1404
trx_sig_reply(sig, next_thr);
1405
trx_sig_remove(trx, sig);
1413
/********************************************************************
1414
Send the reply message when a signal in the queue of the trx has been
1420
trx_sig_t* sig, /* in: signal */
1421
que_thr_t** next_thr) /* in/out: next query thread to run;
1422
if the value which is passed in is
1423
a pointer to a NULL pointer, then the
1424
calling function can start running
1425
a new query thread */
1427
trx_t* receiver_trx;
1430
ut_ad(mutex_own(&kernel_mutex));
1432
if (sig->receiver != NULL) {
1433
ut_ad((sig->receiver)->state == QUE_THR_SIG_REPLY_WAIT);
1435
receiver_trx = thr_get_trx(sig->receiver);
1437
UT_LIST_REMOVE(reply_signals, receiver_trx->reply_signals,
1439
ut_ad(receiver_trx->sess->state != SESS_ERROR);
1441
que_thr_end_wait(sig->receiver, next_thr);
1443
sig->receiver = NULL;
1448
/********************************************************************
1449
Removes a signal object from the trx signal queue. */
1454
trx_t* trx, /* in: trx handle */
1455
trx_sig_t* sig) /* in, own: signal */
1458
ut_ad(mutex_own(&kernel_mutex));
1460
ut_ad(sig->receiver == NULL);
1462
UT_LIST_REMOVE(signals, trx->signals, sig);
1463
sig->type = 0; /* reset the field to catch possible bugs */
1465
if (sig != &(trx->sig)) {
1470
/*************************************************************************
1471
Creates a commit command node struct. */
1476
/* out, own: commit node struct */
1477
mem_heap_t* heap) /* in: mem heap where created */
1479
commit_node_t* node;
1481
node = mem_heap_alloc(heap, sizeof(commit_node_t));
1482
node->common.type = QUE_NODE_COMMIT;
1483
node->state = COMMIT_NODE_SEND;
1488
/***************************************************************
1489
Performs an execution step for a commit type node in a query graph. */
1494
/* out: query thread to run next, or NULL */
1495
que_thr_t* thr) /* in: query thread */
1497
commit_node_t* node;
1498
que_thr_t* next_thr;
1500
node = thr->run_node;
1502
ut_ad(que_node_get_type(node) == QUE_NODE_COMMIT);
1504
if (thr->prev_node == que_node_get_parent(node)) {
1505
node->state = COMMIT_NODE_SEND;
1508
if (node->state == COMMIT_NODE_SEND) {
1509
mutex_enter(&kernel_mutex);
1511
node->state = COMMIT_NODE_WAIT;
1515
thr->state = QUE_THR_SIG_REPLY_WAIT;
1517
/* Send the commit signal to the transaction */
1519
trx_sig_send(thr_get_trx(thr), TRX_SIG_COMMIT, TRX_SIG_SELF,
1520
thr, NULL, &next_thr);
1522
mutex_exit(&kernel_mutex);
1527
ut_ad(node->state == COMMIT_NODE_WAIT);
1529
node->state = COMMIT_NODE_SEND;
1531
thr->run_node = que_node_get_parent(node);
1536
/**************************************************************************
1537
Does the transaction commit for MySQL. */
1540
trx_commit_for_mysql(
1541
/*=================*/
1542
/* out: 0 or error number */
1543
trx_t* trx) /* in: trx handle */
1545
/* Because we do not do the commit by sending an Innobase
1546
sig to the transaction, we must here make sure that trx has been
1551
trx->op_info = "committing";
1553
/* If we are doing the XA recovery of prepared transactions, then
1554
the transaction object does not have an InnoDB session object, and we
1555
set the dummy session that we use for all MySQL transactions. */
1557
if (trx->sess == NULL) {
1558
/* Open a dummy session */
1560
if (!trx_dummy_sess) {
1561
mutex_enter(&kernel_mutex);
1563
if (!trx_dummy_sess) {
1564
trx_dummy_sess = sess_open();
1567
mutex_exit(&kernel_mutex);
1570
trx->sess = trx_dummy_sess;
1573
trx_start_if_not_started(trx);
1575
mutex_enter(&kernel_mutex);
1577
trx_commit_off_kernel(trx);
1579
mutex_exit(&kernel_mutex);
1586
/**************************************************************************
1587
If required, flushes the log to disk if we called trx_commit_for_mysql()
1588
with trx->flush_log_later == TRUE. */
1591
trx_commit_complete_for_mysql(
1592
/*==========================*/
1593
/* out: 0 or error number */
1594
trx_t* trx) /* in: trx handle */
1596
dulint lsn = trx->commit_lsn;
1600
trx->op_info = "flushing log";
1602
if (!trx->must_flush_log_later) {
1604
} else if (srv_flush_log_at_trx_commit == 0) {
1606
} else if (srv_flush_log_at_trx_commit == 1) {
1607
if (srv_unix_file_flush_method == SRV_UNIX_NOSYNC) {
1608
/* Write the log but do not flush it to disk */
1610
log_write_up_to(lsn, LOG_WAIT_ONE_GROUP, FALSE);
1612
/* Write the log to the log files AND flush them to
1615
log_write_up_to(lsn, LOG_WAIT_ONE_GROUP, TRUE);
1617
} else if (srv_flush_log_at_trx_commit == 2) {
1619
/* Write the log but do not flush it to disk */
1621
log_write_up_to(lsn, LOG_WAIT_ONE_GROUP, FALSE);
1626
trx->must_flush_log_later = FALSE;
1633
/**************************************************************************
1634
Marks the latest SQL statement ended. */
1637
trx_mark_sql_stat_end(
1638
/*==================*/
1639
trx_t* trx) /* in: trx handle */
1643
if (trx->conc_state == TRX_NOT_STARTED) {
1644
trx->undo_no = ut_dulint_zero;
1647
trx->last_sql_stat_start.least_undo_no = trx->undo_no;
1650
/**************************************************************************
1651
Prints info about a transaction to the given file. The caller must own the
1652
kernel mutex and must have called
1653
innobase_mysql_prepare_print_arbitrary_thd(), unless he knows that MySQL
1654
or InnoDB cannot meanwhile change the info printed here. */
1659
FILE* f, /* in: output stream */
1660
trx_t* trx, /* in: transaction */
1661
ulint max_query_len) /* in: max query length to print, or 0 to
1662
use the default max length */
1666
fprintf(f, "TRANSACTION %lu %lu",
1667
(ulong) ut_dulint_get_high(trx->id),
1668
(ulong) ut_dulint_get_low(trx->id));
1670
switch (trx->conc_state) {
1671
case TRX_NOT_STARTED:
1672
fputs(", not started", f);
1675
fprintf(f, ", ACTIVE %lu sec",
1676
(ulong)difftime(time(NULL), trx->start_time));
1679
fprintf(f, ", ACTIVE (PREPARED) %lu sec",
1680
(ulong)difftime(time(NULL), trx->start_time));
1682
case TRX_COMMITTED_IN_MEMORY:
1683
fputs(", COMMITTED IN MEMORY", f);
1686
fprintf(f, " state %lu", (ulong) trx->conc_state);
1690
fprintf(f, ", process no %lu", trx->mysql_process_no);
1692
fprintf(f, ", OS thread id %lu",
1693
(ulong) os_thread_pf(trx->mysql_thread_id));
1695
if (*trx->op_info) {
1697
fputs(trx->op_info, f);
1700
if (trx->is_purge) {
1701
fputs(" purge trx", f);
1704
if (trx->declared_to_be_inside_innodb) {
1705
fprintf(f, ", thread declared inside InnoDB %lu",
1706
(ulong) trx->n_tickets_to_enter_innodb);
1711
if (trx->n_mysql_tables_in_use > 0 || trx->mysql_n_tables_locked > 0) {
1712
fprintf(f, "mysql tables in use %lu, locked %lu\n",
1713
(ulong) trx->n_mysql_tables_in_use,
1714
(ulong) trx->mysql_n_tables_locked);
1719
switch (trx->que_state) {
1720
case TRX_QUE_RUNNING:
1721
newline = FALSE; break;
1722
case TRX_QUE_LOCK_WAIT:
1723
fputs("LOCK WAIT ", f); break;
1724
case TRX_QUE_ROLLING_BACK:
1725
fputs("ROLLING BACK ", f); break;
1726
case TRX_QUE_COMMITTING:
1727
fputs("COMMITTING ", f); break;
1729
fprintf(f, "que state %lu ", (ulong) trx->que_state);
1732
if (0 < UT_LIST_GET_LEN(trx->trx_locks)
1733
|| mem_heap_get_size(trx->lock_heap) > 400) {
1736
fprintf(f, "%lu lock struct(s), heap size %lu,"
1738
(ulong) UT_LIST_GET_LEN(trx->trx_locks),
1739
(ulong) mem_heap_get_size(trx->lock_heap),
1740
(ulong) lock_number_of_rows_locked(trx));
1743
if (trx->has_search_latch) {
1745
fputs(", holds adaptive hash latch", f);
1748
if (ut_dulint_cmp(trx->undo_no, ut_dulint_zero) != 0) {
1750
fprintf(f, ", undo log entries %lu",
1751
(ulong) ut_dulint_get_low(trx->undo_no));
1758
if (trx->mysql_thd != NULL) {
1759
innobase_mysql_print_thd(f, trx->mysql_thd, max_query_len);
1763
/***********************************************************************
1764
Compares the "weight" (or size) of two transactions. The weight of one
1765
transaction is estimated as the number of altered rows + the number of
1766
locked rows. Transactions that have edited non-transactional tables are
1767
considered heavier than ones that have not. */
1772
/* out: <0, 0 or >0; similar to strcmp(3) */
1773
trx_t* a, /* in: the first transaction to be compared */
1774
trx_t* b) /* in: the second transaction to be compared */
1776
ibool a_notrans_edit;
1777
ibool b_notrans_edit;
1779
/* If mysql_thd is NULL for a transaction we assume that it has
1780
not edited non-transactional tables. */
1782
a_notrans_edit = a->mysql_thd != NULL
1783
&& thd_has_edited_nontrans_tables(a->mysql_thd);
1785
b_notrans_edit = b->mysql_thd != NULL
1786
&& thd_has_edited_nontrans_tables(b->mysql_thd);
1788
if (a_notrans_edit && !b_notrans_edit) {
1793
if (!a_notrans_edit && b_notrans_edit) {
1798
/* Either both had edited non-transactional tables or both had
1799
not, we fall back to comparing the number of altered/locked
1804
"%s TRX_WEIGHT(a): %lld+%lu, TRX_WEIGHT(b): %lld+%lu\n",
1806
ut_conv_dulint_to_longlong(a->undo_no),
1807
UT_LIST_GET_LEN(a->trx_locks),
1808
ut_conv_dulint_to_longlong(b->undo_no),
1809
UT_LIST_GET_LEN(b->trx_locks));
1812
#define TRX_WEIGHT(t) \
1813
ut_dulint_add((t)->undo_no, UT_LIST_GET_LEN((t)->trx_locks))
1815
return(ut_dulint_cmp(TRX_WEIGHT(a), TRX_WEIGHT(b)));
1818
/********************************************************************
1819
Prepares a transaction. */
1822
trx_prepare_off_kernel(
1823
/*===================*/
1824
trx_t* trx) /* in: transaction */
1826
page_t* update_hdr_page;
1828
ibool must_flush_log = FALSE;
1832
ut_ad(mutex_own(&kernel_mutex));
1836
if (trx->insert_undo != NULL || trx->update_undo != NULL) {
1838
mutex_exit(&kernel_mutex);
1842
must_flush_log = TRUE;
1844
/* Change the undo log segment states from TRX_UNDO_ACTIVE
1845
to TRX_UNDO_PREPARED: these modifications to the file data
1846
structure define the transaction as prepared in the
1847
file-based world, at the serialization point of lsn. */
1849
mutex_enter(&(rseg->mutex));
1851
if (trx->insert_undo != NULL) {
1853
/* It is not necessary to obtain trx->undo_mutex here
1854
because only a single OS thread is allowed to do the
1855
transaction prepare for this transaction. */
1857
trx_undo_set_state_at_prepare(trx, trx->insert_undo,
1861
if (trx->update_undo) {
1862
update_hdr_page = trx_undo_set_state_at_prepare(
1863
trx, trx->update_undo, &mtr);
1866
mutex_exit(&(rseg->mutex));
1869
mtr_commit(&mtr); /* This mtr commit makes the
1870
transaction prepared in the file-based
1875
mutex_enter(&kernel_mutex);
1878
ut_ad(mutex_own(&kernel_mutex));
1880
/*--------------------------------------*/
1881
trx->conc_state = TRX_PREPARED;
1882
/*--------------------------------------*/
1884
if (must_flush_log) {
1885
/* Depending on the my.cnf options, we may now write the log
1886
buffer to the log files, making the prepared state of the
1887
transaction durable if the OS does not crash. We may also
1888
flush the log files to disk, making the prepared state of the
1889
transaction durable also at an OS crash or a power outage.
1891
The idea in InnoDB's group prepare is that a group of
1892
transactions gather behind a trx doing a physical disk write
1893
to log files, and when that physical write has been completed,
1894
one of those transactions does a write which prepares the whole
1895
group. Note that this group prepare will only bring benefit if
1896
there are > 2 users in the database. Then at least 2 users can
1897
gather behind one doing the physical log write to disk.
1899
TODO: find out if MySQL holds some mutex when calling this.
1900
That would spoil our group prepare algorithm. */
1902
mutex_exit(&kernel_mutex);
1904
if (srv_flush_log_at_trx_commit == 0) {
1906
} else if (srv_flush_log_at_trx_commit == 1) {
1907
if (srv_unix_file_flush_method == SRV_UNIX_NOSYNC) {
1908
/* Write the log but do not flush it to disk */
1910
log_write_up_to(lsn, LOG_WAIT_ONE_GROUP,
1913
/* Write the log to the log files AND flush
1916
log_write_up_to(lsn, LOG_WAIT_ONE_GROUP, TRUE);
1918
} else if (srv_flush_log_at_trx_commit == 2) {
1920
/* Write the log but do not flush it to disk */
1922
log_write_up_to(lsn, LOG_WAIT_ONE_GROUP, FALSE);
1927
mutex_enter(&kernel_mutex);
1931
/**************************************************************************
1932
Does the transaction prepare for MySQL. */
1935
trx_prepare_for_mysql(
1936
/*==================*/
1937
/* out: 0 or error number */
1938
trx_t* trx) /* in: trx handle */
1940
/* Because we do not do the prepare by sending an Innobase
1941
sig to the transaction, we must here make sure that trx has been
1946
trx->op_info = "preparing";
1948
trx_start_if_not_started(trx);
1950
mutex_enter(&kernel_mutex);
1952
trx_prepare_off_kernel(trx);
1954
mutex_exit(&kernel_mutex);
1961
/**************************************************************************
1962
This function is used to find number of prepared transactions and
1963
their transaction objects for a recovery. */
1966
trx_recover_for_mysql(
1967
/*==================*/
1968
/* out: number of prepared transactions
1969
stored in xid_list */
1970
XID* xid_list, /* in/out: prepared transactions */
1971
ulint len) /* in: number of slots in xid_list */
1979
/* We should set those transactions which are in the prepared state
1982
mutex_enter(&kernel_mutex);
1984
trx = UT_LIST_GET_FIRST(trx_sys->trx_list);
1987
if (trx->conc_state == TRX_PREPARED) {
1988
xid_list[count] = trx->xid;
1991
ut_print_timestamp(stderr);
1993
" InnoDB: Starting recovery for"
1994
" XA transactions...\n");
1997
ut_print_timestamp(stderr);
1999
" InnoDB: Transaction %lu %lu in"
2000
" prepared state after recovery\n",
2001
(ulong) ut_dulint_get_high(trx->id),
2002
(ulong) ut_dulint_get_low(trx->id));
2004
ut_print_timestamp(stderr);
2006
" InnoDB: Transaction contains changes"
2008
(ulong) ut_conv_dulint_to_longlong(
2018
trx = UT_LIST_GET_NEXT(trx_list, trx);
2021
mutex_exit(&kernel_mutex);
2024
ut_print_timestamp(stderr);
2026
" InnoDB: %lu transactions in prepared state"
2027
" after recovery\n",
2031
return ((int) count);
2034
/***********************************************************************
2035
This function is used to find one X/Open XA distributed transaction
2036
which is in the prepared state */
2041
/* out: trx or NULL */
2042
XID* xid) /* in: X/Open XA transaction identification */
2051
mutex_enter(&kernel_mutex);
2053
trx = UT_LIST_GET_FIRST(trx_sys->trx_list);
2056
/* Compare two X/Open XA transaction id's: their
2057
length should be the same and binary comparison
2058
of gtrid_lenght+bqual_length bytes should be
2061
if (xid->gtrid_length == trx->xid.gtrid_length
2062
&& xid->bqual_length == trx->xid.bqual_length
2063
&& memcmp(xid->data, trx->xid.data,
2064
xid->gtrid_length + xid->bqual_length) == 0) {
2068
trx = UT_LIST_GET_NEXT(trx_list, trx);
2071
mutex_exit(&kernel_mutex);
2074
if (trx->conc_state != TRX_PREPARED) {