File: trx0trx.h

package info (click to toggle)
mysql-8.0 8.0.44-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 1,272,892 kB
  • sloc: cpp: 4,685,345; ansic: 412,712; pascal: 108,395; java: 83,641; perl: 30,221; cs: 27,067; sql: 26,594; python: 21,816; sh: 17,285; yacc: 17,169; php: 11,522; xml: 7,388; javascript: 7,083; makefile: 1,793; lex: 1,075; awk: 670; asm: 520; objc: 183; ruby: 97; lisp: 86
file content (1609 lines) | stat: -rw-r--r-- 63,449 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
/*****************************************************************************

Copyright (c) 1996, 2025, Oracle and/or its affiliates.

This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License, version 2.0, as published by the
Free Software Foundation.

This program is designed to work with certain software (including
but not limited to OpenSSL) that is licensed under separate terms,
as designated in a particular file or component or in included license
documentation.  The authors of MySQL hereby grant you an additional
permission to link the program and your derivative works with the
separately licensed software that they have either included with
the program or referenced in the documentation.

This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License, version 2.0,
for more details.

You should have received a copy of the GNU General Public License along with
this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA

*****************************************************************************/

/** @file include/trx0trx.h
 The transaction

 Created 3/26/1996 Heikki Tuuri
 *******************************************************/

#ifndef trx0trx_h
#define trx0trx_h

#include <atomic>
#include <set>

#include "ha_prototypes.h"

#include "dict0types.h"
#include "trx0types.h"
#include "ut0new.h"

#include "lock0types.h"
#include "mem0mem.h"
#include "que0types.h"
#include "trx0xa.h"
#include "usr0types.h"
#include "ut0vec.h"
#ifndef UNIV_HOTBACKUP
#include "fts0fts.h"
#endif /* !UNIV_HOTBACKUP */
#include "read0read.h"
#include "sql/handler.h"  // Xa_state_list
#include "srv0srv.h"

/* std::vector to store the trx id & table id of tables that needs to be
 * rollbacked. We take SHARED MDL on these tables inside
 * trx_recovery_rollback_thread before letting server accept connections */
extern std::vector<std::pair<trx_id_t, table_id_t>> to_rollback_trx_tables;

// Forward declaration
struct mtr_t;

// Forward declaration
class ReadView;

// Forward declaration
class Flush_observer;

/** Dummy session used currently in MySQL interface */
extern sess_t *trx_dummy_sess;

#ifndef UNIV_HOTBACKUP
/** Set flush observer for the transaction
@param[in,out]  trx             transaction struct
@param[in]      observer        flush observer */
void trx_set_flush_observer(trx_t *trx, Flush_observer *observer);

/** Set detailed error message for the transaction.
@param[in] trx Transaction struct
@param[in] msg Detailed error message */
void trx_set_detailed_error(trx_t *trx, const char *msg);

/** Set detailed error message for the transaction from a file. Note that the
 file is rewinded before reading from it. */
void trx_set_detailed_error_from_file(
    trx_t *trx,  /*!< in: transaction struct */
    FILE *file); /*!< in: file to read message from */
/** Retrieves the error_info field from a trx.
 @return the error index */
static inline const dict_index_t *trx_get_error_index(
    const trx_t *trx); /*!< in: trx object */
/** Creates a transaction object for MySQL.
 @return own: transaction object */
trx_t *trx_allocate_for_mysql(void);
/** Creates a transaction object for background operations by the master thread.
 @return own: transaction object */
trx_t *trx_allocate_for_background(void);

/** Resurrect table locks for resurrected transactions.
@param[in]      all     false: resurrect locks for dictionary transactions,
                        true : resurrect locks for all transactions. */
void trx_resurrect_locks(bool all);

/** Clear all resurrected table IDs. Needs to be called after all tables locks
are resurrected. */
void trx_clear_resurrected_table_ids();

/** Free and initialize a transaction object instantiated during recovery.
@param[in,out]  trx     transaction object to free and initialize */
void trx_free_resurrected(trx_t *trx);

/** Free a transaction that was allocated by background or user threads.
@param[in,out]  trx     transaction object to free */
void trx_free_for_background(trx_t *trx);

/** At shutdown, frees a transaction object that represents either:
 - a PREPARED transaction,
 - or a recovered ACTIVE transaction.
@param[in, out]  trx   transaction object to free */
void trx_free_prepared_or_active_recovered(trx_t *trx);

/** Free a transaction object for MySQL.
@param[in,out]  trx     transaction */
void trx_free_for_mysql(trx_t *trx);

/** Disconnect a transaction from MySQL.
@param[in,out]  trx     transaction */
void trx_disconnect_plain(trx_t *trx);

/** Disconnect a prepared transaction from MySQL.
@param[in,out]  trx     transaction */
void trx_disconnect_prepared(trx_t *trx);

/** Creates trx objects for transactions and initializes the trx list of
 trx_sys at database start. Rollback segment and undo log lists must
 already exist when this function is called, because the lists of
 transactions to be rolled back or cleaned up are built based on the
 undo log lists. */
void trx_lists_init_at_db_start(void);

/** Starts the transaction if it is not yet started.
@param[in,out] trx Transaction
@param[in] read_write True if read write transaction */
void trx_start_if_not_started_xa_low(trx_t *trx, bool read_write);

/** Starts the transaction if it is not yet started.
@param[in] trx Transaction
@param[in] read_write True if read write transaction */
void trx_start_if_not_started_low(trx_t *trx, bool read_write);

/** Starts a transaction for internal processing. */
void trx_start_internal_low(trx_t *trx); /*!< in/out: transaction */

/** Starts a read-only transaction for internal processing.
@param[in,out] trx      transaction to be started */
void trx_start_internal_read_only_low(trx_t *trx);

/** Commits a transaction. */
void trx_commit(trx_t *trx); /*!< in/out: transaction */

/** Commits a transaction and a mini-transaction.
@param[in,out] trx Transaction
@param[in,out] mtr Mini-transaction (will be committed), or null if trx made no
modifications */
void trx_commit_low(trx_t *trx, mtr_t *mtr);

/** Cleans up a transaction at database startup. The cleanup is needed if
 the transaction already got to the middle of a commit when the database
 crashed, and we cannot roll it back. */
void trx_cleanup_at_db_startup(trx_t *trx); /*!< in: transaction */
/** Does the transaction commit for MySQL.
 @return DB_SUCCESS or error number */
dberr_t trx_commit_for_mysql(trx_t *trx); /*!< in/out: transaction */

/**
Does the transaction prepare for MySQL.
@param[in, out] trx             Transaction instance to prepare */

dberr_t trx_prepare_for_mysql(trx_t *trx);

/** This function is used to find number of prepared transactions and
 their transaction objects for a recovery.
 @return number of prepared transactions */
int trx_recover_for_mysql(
    XA_recover_txn *txn_list, /*!< in/out: prepared transactions */
    ulint len,                /*!< in: number of slots in xid_list */
    MEM_ROOT *mem_root);      /*!< in: memory for table names */

/** Find prepared transactions that are marked as prepared in TC, for recovery
purposes.
@param[in,out] xa_list prepared transactions state
@return 0 if successful or error number */
int trx_recover_tc_for_mysql(Xa_state_list &xa_list);

/** This function is used to find one X/Open XA distributed transaction
 which is in the prepared state
 @param[in]   xid   X/Open XA transaction identifier
 @return trx or NULL; on match, the trx->xid will be invalidated;
 note that the trx may have been committed */
trx_t *trx_get_trx_by_xid(const XID *xid);

/** If required, flushes the log to disk if we called trx_commit_for_mysql()
 with trx->flush_log_later == true. */
void trx_commit_complete_for_mysql(trx_t *trx); /*!< in/out: transaction */
/** Marks the latest SQL statement ended. */
void trx_mark_sql_stat_end(trx_t *trx); /*!< in: trx handle */
/** Assigns a read view for a consistent read query. All the consistent reads
 within the same transaction will get the same read view, which is created
 when this function is first called for a new started transaction. */
ReadView *trx_assign_read_view(trx_t *trx); /*!< in: active transaction */

/** @return the transaction's read view or NULL if one not assigned. */
static inline ReadView *trx_get_read_view(trx_t *trx);

/** @return the transaction's read view or NULL if one not assigned. */
static inline const ReadView *trx_get_read_view(const trx_t *trx);

/** Prepares a transaction for commit/rollback. */
void trx_commit_or_rollback_prepare(trx_t *trx); /*!< in/out: transaction */
/** Creates a commit command node struct.
 @return own: commit node struct */
commit_node_t *trx_commit_node_create(
    mem_heap_t *heap); /*!< in: mem heap where created */
/** Performs an execution step for a commit type node in a query graph.
 @return query thread to run next, or NULL */
que_thr_t *trx_commit_step(que_thr_t *thr); /*!< in: query thread */

/** Prints info about a transaction.
 Caller must hold trx_sys->mutex. */
void trx_print_low(FILE *f,
                   /*!< in: output stream */
                   const trx_t *trx,
                   /*!< in: transaction */
                   ulint max_query_len,
                   /*!< in: max query length to print,
                   must be positive */
                   ulint n_rec_locks,
                   /*!< in: lock_number_of_rows_locked(&trx->lock) */
                   ulint n_trx_locks,
                   /*!< in: length of trx->lock.trx_locks */
                   ulint heap_size);
/*!< in: mem_heap_get_size(trx->lock.lock_heap) */

/** Prints info about a transaction.
The caller must hold lock_sys exclusive global latch and trx_sys->mutex.
@param[in]  f               output stream
@param[in]  trx             transaction
@param[in]  max_query_len   max query length to print, must be positive */
void trx_print_latched(FILE *f, const trx_t *trx, ulint max_query_len);

/** Prints info about a transaction.
Acquires and releases lock_sys exclusive global latch and trx_sys->mutex.
@param[in]  f               output stream
@param[in]  trx             transaction
@param[in]  max_query_len   max query length to print, must be positive */
void trx_print(FILE *f, const trx_t *trx, ulint max_query_len);

/** Determine if a transaction is a dictionary operation.
 @return dictionary operation mode */
[[nodiscard]] static inline enum trx_dict_op_t trx_get_dict_operation(
    const trx_t *trx); /*!< in: transaction */

/** Flag a transaction a dictionary operation.
@param[in,out]  trx     transaction
@param[in]      op      operation, not TRX_DICT_OP_NONE */
static inline void trx_set_dict_operation(trx_t *trx, enum trx_dict_op_t op);

/** Determines if a transaction is in the given state.
The caller must hold trx_sys->mutex, or it must be the thread
that is serving a running transaction.
A running RW transaction must be in trx_sys->rw_trx_list.
@param[in] trx   Transaction.
@param[in] state State.
@return true if trx->state == state */
[[nodiscard]] static inline bool trx_state_eq(const trx_t *trx,
                                              trx_state_t state);
#ifdef UNIV_DEBUG
/** Determines if trx can be handled by current thread, which is when
trx->mysql_thd is nullptr (a "background" trx) or equals current_thd.
@param[in]    trx   The transaction to check
@return true iff current thread can handle the transaction
*/
bool trx_can_be_handled_by_current_thread(const trx_t *trx);

/** Determines if trx can be handled by current thread, which is when
trx->mysql_thd is nullptr (a "background" trx) or equals current_thd,
or is a victim being killed by HP transaction run by the current thread.
@param[in]    trx   The transaction to check
@return true iff current thread can handle the transaction
*/
bool trx_can_be_handled_by_current_thread_or_is_hp_victim(const trx_t *trx);

/** Asserts that a transaction has been started.
 The caller must hold trx_sys->mutex.
 @return true if started */
[[nodiscard]] bool trx_assert_started(const trx_t *trx); /*!< in: transaction */
#endif                                                   /* UNIV_DEBUG */

/** Determines if the currently running transaction has been interrupted.
 @return true if interrupted */
bool trx_is_interrupted(const trx_t *trx); /*!< in: transaction */
/** Determines if the currently running transaction is in strict mode.
 @return true if strict */
bool trx_is_strict(trx_t *trx); /*!< in: transaction */

/** Compares the "weight" (or size) of two transactions. Transactions that
 have edited non-transactional tables are considered heavier than ones
 that have not.
 @return true if weight(a) >= weight(b) */
bool trx_weight_ge(const trx_t *a,  /*!< in: the transaction to be compared */
                   const trx_t *b); /*!< in: the transaction to be compared */
/* Maximum length of a string that can be returned by
trx_get_que_state_str(). */
constexpr uint32_t TRX_QUE_STATE_STR_MAX_LEN = 12; /* "ROLLING BACK" */

/** Retrieves transaction's que state in a human readable string. The string
 should not be free()'d or modified.
 @return string in the data segment */
static inline const char *trx_get_que_state_str(
    const trx_t *trx); /*!< in: transaction */

/** Retreieves the transaction ID.
In a given point in time it is guaranteed that IDs of the running
transactions are unique. The values returned by this function for readonly
transactions may be reused, so a subsequent RO transaction may get the same ID
as a RO transaction that existed in the past. The values returned by this
function should be used for printing purposes only.
@param[in]      trx     transaction whose id to retrieve
@return transaction id */
static inline trx_id_t trx_get_id_for_print(const trx_t *trx);

/** Assign a temp-tablespace bound rollback-segment to a transaction.
@param[in,out]  trx     transaction that involves write to temp-table. */
void trx_assign_rseg_temp(trx_t *trx);

/** Create the trx_t pool */
void trx_pool_init();

/** Destroy the trx_t pool */
void trx_pool_close();

/**
Set the transaction as a read-write transaction if it is not already
tagged as such.
@param[in,out] trx      Transaction that needs to be "upgraded" to RW from RO */
void trx_set_rw_mode(trx_t *trx);

/**
@param[in] requestor    Transaction requesting the lock
@param[in] holder       Transaction holding the lock
@return the transaction that will be rolled back, null don't care */

static inline const trx_t *trx_arbitrate(const trx_t *requestor,
                                         const trx_t *holder);

/**
@param[in] trx          Transaction to check
@return true if the transaction is a high priority transaction.*/
static inline bool trx_is_high_priority(const trx_t *trx);

/**
If this is a high priority transaction,
kill all transactions that are blocking this transaction from acquiring locks.
@param[in,out] trx      High priority transaction */
void trx_kill_blocking(trx_t *trx);

/**
Check if redo/noredo rseg is modified for insert/update.
@param[in] trx          Transaction to check */
static inline bool trx_is_rseg_updated(const trx_t *trx);
#endif /* !UNIV_HOTBACKUP */

typedef std::vector<ib_lock_t *, ut::allocator<ib_lock_t *>> lock_pool_t;

/** Latching protocol for trx_lock_t::que_state.  trx_lock_t::que_state
 captures the state of the query thread during the execution of a query.
 This is different from a transaction state. The query state of a transaction
 can be updated asynchronously by other threads.  The other threads can be
 system threads, like the timeout monitor thread or user threads executing
 other queries. Another thing to be mindful of is that there is a delay between
 when a query thread is put into LOCK_WAIT state and before it actually starts
 waiting.  Between these two events it is possible that the query thread is
 granted the lock it was waiting for, which implies that the state can be
 changed asynchronously.

 All these operations take place within the context of locking. Therefore state
 changes within the locking code must latch the shard with the wait_lock and
 the trx->mutex when changing trx->lock.que_state to TRX_QUE_LOCK_WAIT or
 trx->lock.wait_lock to non-NULL but when the lock wait ends it is sufficient
 to only acquire the trx->mutex.
 To query the state either of the mutexes is sufficient within the locking
 code and no mutex is required when the query thread is no longer waiting. */

/** The locks and state of an active transaction.
Protected by exclusive lock_sys latch or trx->mutex combined with shared
lock_sys latch (unless stated otherwise for particular field). */
struct trx_lock_t {
  /** Default constructor. */
  trx_lock_t() = default;

  ulint n_active_thrs; /*!< number of active query threads */

  trx_que_t que_state; /*!< valid when trx->state
                       == TRX_STATE_ACTIVE: TRX_QUE_RUNNING,
                       TRX_QUE_LOCK_WAIT, ... */

  /** Incremented each time a lock is added or removed from the
  trx->lock.trx_locks, so that the thread which iterates over the list can spot
  a change if it occurred while it was reacquiring latches.
  Protected by trx->mutex. */
  uint64_t trx_locks_version;

  /** If this transaction is waiting for a lock, then blocking_trx points to a
  transaction which holds a conflicting lock.
  It is possible that the transaction has trx->lock.wait_lock == nullptr, yet it
  has non-null value of trx->lock.blocking_trx. For example this can happen when
  we are in the process of moving locks from one heap_no to another. This
  however is always done while the lock_sys shards which contain the queues
  involved are latched and conceptually it is true that the blocking_trx is
  the one for which the transaction waits, even though temporarily there is no
  pointer to a particular WAITING lock object.

  This field is changed from null to non-null, when holding this->mutex and
  mutex for lock_sys shard containing the new value of trx->lock.wait_lock.
  The field is changed from non-null to different non-null value, while holding
  mutex for lock_sys shard containing the trx->lock.wait_lock.
  The field is changed from non-null to null, while holding this->mutex,
  mutex for lock_sys shard containing the old value of trx->lock.wait_lock,
  before it was changed to null.

  Readers might read it without any latch, but then they should validate the
  value, i.e. test if it is not-null, and points to a valid trx.
  To make any definite judgments one needs to latch the lock_sys shard
  containing the trx->lock.wait_lock. */
  std::atomic<trx_t *> blocking_trx;

  /** The lock request of this transaction is waiting for.
  It might be NULL if the transaction is not currently waiting, or if the lock
  was temporarily removed during B-tree reorganization and will be recreated in
  a different queue. Such move is protected by latching the shards containing
  both queues, so the intermediate state should not be observed by readers who
  latch the old shard.

  Changes from NULL to non-NULL while holding trx->mutex and latching the shard
  containing the new wait_lock value.
  Changes from non-NULL to NULL while latching the shard containing the old
  wait_lock value.
  Never changes from non-NULL to other non-NULL directly.

  Readers should hold exclusive global latch on lock_sys, as in general they
  can't know what shard the lock belongs to before reading it.
  However, in debug assertions, where we strongly believe to know the value of
  this field in advance, we can:
  - read without any latch if we believe the value should be NULL
  - read latching only the shard containing the wait_lock we expect */
  std::atomic<lock_t *> wait_lock;

  /** Stores the type of the most recent lock for which this trx had to wait.
  Set to lock_get_type_low(wait_lock) together with wait_lock in
  lock_set_lock_and_trx_wait().
  This field is not cleared when wait_lock is set to NULL during
  lock_reset_lock_and_trx_wait() as in lock_wait_suspend_thread() we are
  interested in reporting the last known value of this field via
  thd_wait_begin(). When a thread has to wait for a lock, it first releases
  lock-sys latch, and then calls lock_wait_suspend_thread() where among other
  things it tries to report statistic via thd_wait_begin() about the kind of
  lock (THD_WAIT_ROW_LOCK vs THD_WAIT_TABLE_LOCK) that caused the wait. But
  there is a possibility that before it gets to call thd_wait_begin() some other
  thread could latch lock-sys and grant the lock and call
  lock_reset_lock_and_trx_wait(). In other words: in case another thread was
  quick enough to grant the lock, we still would like to report the reason for
  attempting to sleep.
  Another common scenario of "setting trx->lock.wait_lock to NULL" is page
  reorganization: when we have to move records between pages, we also move
  locks, and when doing so, we temporarily remove the old waiting lock, and then
  add another one. For example look at lock_rec_move_low(). It first calls
  lock_reset_lock_and_trx_wait() which changes trx->lock.wait_lock to NULL, but
  then calls lock_rec_add_to_queue() -> RecLock::create() -> RecLock::lock_add()
  -> lock_set_lock_and_trx_wait() to set it again to the new lock. This all
  happens while holding lock-sys latch, but we read wait_lock_type without this
  latch, so we should not clear the wait_lock_type simply because somebody
  changed wait_lock to NULL.
  Protected by trx->mutex. */
  uint32_t wait_lock_type;

  bool was_chosen_as_deadlock_victim;
  /*!< when the transaction decides to
  wait for a lock, it sets this to false;
  if another transaction chooses this
  transaction as a victim in deadlock
  resolution, it sets this to true.
  Protected by trx->mutex. */

  /** Lock wait started at this time.
  Writes under shared lock_sys latch combined with trx->mutex.
  Reads require either trx->mutex or exclusive lock_sys latch. */
  std::chrono::system_clock::time_point wait_started;

  /** query thread belonging to this trx that is in QUE_THR_LOCK_WAIT state.
  For threads suspended in a lock wait, this is protected by lock_sys latch for
  the wait_lock's shard.
  Otherwise, this may only be modified by the thread that is serving the running
  transaction.
  */
  que_thr_t *wait_thr;

  /** Pre-allocated record locks. Protected by trx->mutex. */
  lock_pool_t rec_pool;

  /** Pre-allocated table locks. Protected by trx->mutex. */
  lock_pool_t table_pool;

  /** Next free record lock in pool. Protected by trx->mutex. */
  ulint rec_cached;

  /** Next free table lock in pool. Protected by trx->mutex. */
  ulint table_cached;

  /** Memory heap for trx_locks. Protected by trx->mutex */
  mem_heap_t *lock_heap;

  /** Locks requested by the transaction.
  It is sorted so that LOCK_TABLE locks are before LOCK_REC locks.
  Modifications are protected by trx->mutex and shard of lock_sys mutex.
  Reads can be performed while holding trx->mutex or exclusive lock_sys latch.
  One can also check if this list is empty or not from the thread running this
  transaction without holding any latches, keeping in mind that other threads
  might modify the list in parallel (for example during implicit-to-explicit
  conversion, or when B-tree split or merge causes locks to be moved from one
  page to another) - we rely on assumption that such operations do not change
  the "emptiness" of the list and that one can check for emptiness in a safe
  manner (in current implementation length of the list is stored explicitly so
  one can read it without risking unsafe pointer operations) */
  trx_lock_list_t trx_locks;

  /** AUTOINC locks held by this transaction.
  Note that these are also in the trx_locks list.
  This vector needs to be freed explicitly when the trx instance is destroyed.
  Protected by trx->mutex. */
  ib_vector_t *autoinc_locks;

  /** Number of rec locks in this trx.
  It is modified with shared lock_sys latch.
  It is read with exclusive lock_sys latch. */
  std::atomic<ulint> n_rec_locks;

  /** Used to indicate that every lock of this transaction placed on a record
  which is being purged should be inherited to the gap.
  Readers should hold a latch on the lock they'd like to learn about whether or
  not it should be inherited.
  Writers who want to set it to true, should hold a latch on the lock-sys queue
  they intend to add a lock to.
  Writers may set it to false at any time. */
  std::atomic<bool> inherit_all;

  /** Weight of the waiting transaction used for scheduling.
  The higher the weight the more we are willing to grant a lock to this
  transaction.
  Values are updated and read without any synchronization beyond that provided
  by atomics, as slightly stale values do not hurt correctness, just the
  performance. */
  std::atomic<trx_schedule_weight_t> schedule_weight;

#ifdef UNIV_DEBUG
  /** When a transaction is forced to rollback due to a deadlock
  check or by another high priority transaction this is true. Used
  by debug checks in lock0lock.cc */
  bool in_rollback;
#endif /* UNIV_DEBUG */

  /** The transaction called ha_innobase::start_stmt() to
  lock a table. Most likely a temporary table. */
  bool start_stmt;
};

/** Type used to store the list of tables that are modified by a given
transaction. We store pointers to the table objects in memory because
we know that a table object will not be destroyed while a transaction
that modified it is running. */
typedef std::set<dict_table_t *, std::less<dict_table_t *>,
                 ut::allocator<dict_table_t *>>
    trx_mod_tables_t;

/** The transaction handle

Normally, there is a 1:1 relationship between a transaction handle
(trx) and a session (client connection). One session is associated
with exactly one user transaction. There are some exceptions to this:

* For DDL operations, a subtransaction is allocated that modifies the
data dictionary tables. Lock waits and deadlocks are prevented by
acquiring the dict_operation_lock before starting the subtransaction
and releasing it after committing the subtransaction.

* The purge system uses a special transaction that is not associated
with any session.

* If the system crashed or it was quickly shut down while there were
transactions in the ACTIVE or PREPARED state, these transactions would
no longer be associated with a session when the server is restarted.

A session may be served by at most one thread at a time. The serving
thread of a session might change in some MySQL implementations.
Therefore we do not have std::this_thread::get_id() assertions in the code.

Normally, only the thread that is currently associated with a running
transaction may access (read and modify) the trx object, and it may do
so without holding any mutex. The following are exceptions to this:

* trx_rollback_resurrected() may access resurrected (connectionless)
transactions while the system is already processing new user
transactions. The trx_sys->mutex prevents a race condition between it
and lock_trx_release_locks() [invoked by trx_commit()].

* Print of transactions may access transactions not associated with
the current thread. The caller must be holding trx_sys->mutex and
exclusive global lock_sys latch.

* When a transaction handle is in the trx_sys->mysql_trx_list or
trx_sys->trx_list, some of its fields must not be modified without
holding trx_sys->mutex exclusively.

* The locking code (in particular, deadlock checking and implicit to
explicit conversion) will access transactions associated to other
connections. The locks of transactions are protected by lock_sys latches
and sometimes by trx->mutex.

* Killing of asynchronous transactions. */

/** Represents an instance of rollback segment along with its state variables.*/
struct trx_undo_ptr_t {
  /** @return true iff no undo segment is allocated yet. */
  bool is_empty() { return (insert_undo == nullptr && update_undo == nullptr); }

  /** @return true iff only insert undo segment is allocated. */
  bool is_insert_only() {
    return (insert_undo != nullptr && update_undo == nullptr);
  }

  /** @return true iff update undo segment is allocated. */
  bool is_update() { return update_undo != nullptr; }

  trx_rseg_t *rseg;        /*!< rollback segment assigned to the
                           transaction, or NULL if not assigned
                           yet */
  trx_undo_t *insert_undo; /*!< pointer to the insert undo log, or
                           NULL if no inserts performed yet */
  trx_undo_t *update_undo; /*!< pointer to the update undo log, or
                           NULL if no update performed yet */
};

/** Rollback segments assigned to a transaction for undo logging. */
struct trx_rsegs_t {
  /** undo log ptr holding reference to a rollback segment that resides in
  system/undo tablespace used for undo logging of tables that needs
  to be recovered on crash. */
  trx_undo_ptr_t m_redo;

  /** undo log ptr holding reference to a rollback segment that resides in
  temp tablespace used for undo logging of tables that doesn't need
  to be recovered on crash. */
  trx_undo_ptr_t m_noredo;
};

enum trx_rseg_type_t {
  TRX_RSEG_TYPE_NONE = 0, /*!< void rollback segment type. */
  TRX_RSEG_TYPE_REDO,     /*!< redo rollback segment. */
  TRX_RSEG_TYPE_NOREDO    /*!< non-redo rollback segment. */
};

struct trx_t {
  enum isolation_level_t {

    /** dirty read: non-locking SELECTs are performed so that we
    do not look at a possible earlier version of a record; thus
    they are not 'consistent' reads under this isolation level;
    otherwise like level 2 */
    READ_UNCOMMITTED,

    /** somewhat Oracle-like isolation, except that in range UPDATE
    and DELETE we must block phantom rows with next-key locks;
    SELECT ... FOR UPDATE and ...  LOCK IN SHARE MODE only lock
    the index records, NOT the gaps before them, and thus allow
    free inserting; each consistent read reads its own snapshot */
    READ_COMMITTED,

    /** this is the default; all consistent reads in the same trx
    read the same snapshot; full next-key locking used in locking
    reads to block insertions into gaps */
    REPEATABLE_READ,

    /** all plain SELECTs are converted to LOCK IN SHARE MODE
    reads */
    SERIALIZABLE
  };

  /** Default constructor */
  trx_t() = default;

  /** Mutex protecting the fields `state` and `lock` (except some fields of
  `lock`,  which are protected by lock_sys latches) */
  mutable TrxMutex mutex;

  /* Note: in_depth was split from in_innodb for fixing a RO
  performance issue. Acquiring the trx_t::mutex for each row
  costs ~3% in performance. It is not required for correctness.
  Therefore we increment/decrement in_depth without holding any
  mutex. The assumption is that the Server will only ever call
  the handler from one thread. This is not true for kill_connection.
  Therefore in innobase_kill_connection. We don't increment this
  counter via TrxInInnoDB. */

  uint32_t in_depth; /*!< Track nested TrxInInnoDB
                        count */

  uint32_t in_innodb; /*!< if the thread is executing
                         in the InnoDB context count > 0. */

  bool abort; /*!< if this flag is set then
              this transaction must abort when
              it can */

  trx_id_t id; /*!< transaction id */

  trx_id_t no; /*!< transaction serialization number:
               max trx id shortly before the
               transaction is moved to
               COMMITTED_IN_MEMORY state.
               Protected by trx_sys_t::mutex
               when trx->in_rw_trx_list. Initially
               set to TRX_ID_MAX. */

  /** State of the trx from the point of view of concurrency control
  and the valid state transitions.

  Possible states:

  TRX_STATE_NOT_STARTED
  TRX_STATE_FORCED_ROLLBACK
  TRX_STATE_ACTIVE
  TRX_STATE_PREPARED
  TRX_STATE_COMMITTED_IN_MEMORY (alias below COMMITTED)

  Valid state transitions are:

  Regular transactions:
  * NOT_STARTED -> ACTIVE -> COMMITTED -> NOT_STARTED

  Auto-commit non-locking read-only:
  * NOT_STARTED -> ACTIVE -> NOT_STARTED

  XA (2PC):
  * NOT_STARTED -> ACTIVE -> PREPARED -> COMMITTED -> NOT_STARTED

  Recovered XA:
  * NOT_STARTED -> PREPARED -> COMMITTED -> (freed)

  XA (2PC) (shutdown or disconnect before ROLLBACK or COMMIT):
  * NOT_STARTED -> PREPARED -> (freed)

  Disconnected XA can become recovered:
  * ... -> ACTIVE -> PREPARED (connected) -> PREPARED (disconnected)
  Disconnected means from mysql e.g due to the mysql client disconnection.
  Latching and various transaction lists membership rules:

  XA (2PC) transactions are always treated as non-autocommit.

  Transitions to ACTIVE or NOT_STARTED occur when
  !in_rw_trx_list (no trx_sys->mutex needed).

  Autocommit non-locking read-only transactions move between states
  without holding any mutex. They are !in_rw_trx_list.

  All transactions, unless they are determined to be ac-nl-ro,
  explicitly tagged as read-only or read-write, will first be put
  on the read-only transaction list. Only when a !read-only transaction
  in the read-only list tries to acquire an X or IX lock on a table
  do we remove it from the read-only list and put it on the read-write
  list. During this switch we assign it a rollback segment.

  When a transaction is NOT_STARTED, it can be in_mysql_trx_list if
  it is a user transaction. It cannot be in rw_trx_list.

  ACTIVE->PREPARED->COMMITTED is only possible when trx->in_rw_trx_list.
  The transition ACTIVE->PREPARED is protected by trx_sys->mutex.

  ACTIVE->COMMITTED is possible when the transaction is in
  rw_trx_list.

  Transitions to COMMITTED are protected by trx->mutex.

  NOTE: Some of these state change constraints are an overkill,
  currently only required for a consistent view for printing stats.
  This unnecessarily adds a huge cost for the general case. */

  std::atomic<trx_state_t> state;

  /* If set, this transaction should stop inheriting (GAP)locks.
  Generally set to true during transaction prepare for RC or lower
  isolation, if requested. Needed for replication replay where
  we don't want to get blocked on GAP locks taken for protecting
  concurrent unique insert or replace operation. */
  bool skip_lock_inheritance;

  ReadView *read_view; /*!< consistent read view used in the
                       transaction, or NULL if not yet set */

  UT_LIST_NODE_T(trx_t)
  trx_list; /*!< list of transactions;
            protected by trx_sys->mutex. */
  UT_LIST_NODE_T(trx_t)
  no_list; /*!< Required during view creation
           to check for the view limit for
           transactions that are committing */

  /** Information about the transaction locks and state.
  Protected by trx->mutex or lock_sys latches or both */
  trx_lock_t lock;

  /**
  false:  a normal transaction
  true:   a recovered transaction

  Set to true when srv_is_being_started for recovered transactions.
  Set to false without any protection in trx_init (where no other thread should
  access this object anyway).
  Can be read safely when holding trx_sys->mutex and trx belongs to rw_trx_list,
  as trx_init can not be called until trx leaves rw_trx_list which requires the
  trx_sys->mutex.
  */
  bool is_recovered;

  std::atomic<std::thread::id> killed_by; /*!< The thread ID that wants to
                            kill this transaction asynchronously.
                            This is required because we recursively
                            enter the handlerton methods and need
                            to distinguish between the kill thread
                            and the transaction thread.

                            Note: We need to be careful w.r.t the
                            Thread Pool. The thread doing the kill
                            should not leave InnoDB between the
                            mark and the actual async kill because
                            the running thread can change. */

  /* These fields are not protected by any mutex. */
  const char *op_info; /*!< English text describing the
                       current operation, or an empty
                       string */

  /** Current isolation level */
  isolation_level_t isolation_level;

  bool check_foreigns; /*!< normally true, but if the user
                       wants to suppress foreign key checks,
                       (in table imports, for example) we
                       set this false */
  /*------------------------------*/
  /* MySQL has a transaction coordinator to coordinate two phase
  commit between multiple storage engines and the binary log. When
  an engine participates in a transaction, it's responsible for
  registering itself using the trans_register_ha() API. */
  bool is_registered; /* This flag is set to true after the
                      transaction has been registered with
                      the coordinator using the XA API, and
                      is set to false  after commit or
                      rollback. */
  /*------------------------------*/
  bool check_unique_secondary;
  /*!< normally true, but if the user
  wants to speed up inserts by
  suppressing unique key checks
  for secondary indexes when we decide
  if we can use the insert buffer for
  them, we set this false */
  bool flush_log_later;      /* In 2PC, we hold the
                             prepare_commit mutex across
                             both phases. In that case, we
                             defer flush of the logs to disk
                             until after we release the
                             mutex. */
  bool must_flush_log_later; /*!< this flag is set to true in
                        trx_commit() if flush_log_later was
                        true, and there were modifications by
                        the transaction; in that case we must
                        flush the log in
                        trx_commit_complete_for_mysql() */
  bool has_search_latch;
  /*!< true if this trx has latched the
  search system latch in S-mode.
  This now can only be true in
  row_search_mvcc, the btr search latch
  must has been released before exiting,
  and this flag would be set to false */
  trx_dict_op_t dict_operation; /**< @see enum trx_dict_op_t */

  bool ddl_operation;  /*!< True if this trx involves dd table
                        change */
  bool ddl_must_flush; /*!< True if this trx involves dd table
                       change, and must flush */
  bool in_truncate;    /* This trx is doing truncation */

  /* Fields protected by the srv_conc_mutex. */
  bool declared_to_be_inside_innodb;
  /*!< this is true if we have declared
  this transaction in
  srv_conc_enter_innodb to be inside the
  InnoDB engine */
  uint32_t n_tickets_to_enter_innodb;
  /*!< this can be > 0 only when
  declared_to_... is true; when we come
  to srv_conc_innodb_enter, if the value
  here is > 0, we decrement this by 1 */
  uint32_t dict_operation_lock_mode;
  /*!< 0, RW_S_LATCH, or RW_X_LATCH:
  the latch mode trx currently holds
  on dict_operation_lock. Protected
  by dict_operation_lock. */

  /** Time the state last time became TRX_STATE_ACTIVE. */
  std::atomic<std::chrono::system_clock::time_point> start_time{
      std::chrono::system_clock::time_point{}};
  static_assert(decltype(start_time)::is_always_lock_free);

  lsn_t commit_lsn; /*!< lsn at the time of the commit */

  /*------------------------------*/
  THD *mysql_thd; /*!< MySQL thread handle corresponding
                  to this trx, or NULL */

  const char *mysql_log_file_name;
  /*!< if MySQL binlog is used, this field
  contains a pointer to the latest file
  name; this is NULL if binlog is not
  used */
  uint64_t mysql_log_offset;
  /*!< if MySQL binlog is used, this
  field contains the end offset of the
  binlog entry */
  /*------------------------------*/
  uint32_t n_mysql_tables_in_use; /*!< number of Innobase tables
                              used in the processing of the current
                              SQL statement in MySQL */
  uint32_t mysql_n_tables_locked;
  /*!< how many tables the current SQL
  statement uses, except those
  in consistent read */
  /*------------------------------*/
#ifdef UNIV_DEBUG
  /** True iff in trx_sys->rw_trx_list */
  bool in_rw_trx_list;

#endif /* UNIV_DEBUG */
  UT_LIST_NODE_T(trx_t)
  mysql_trx_list; /*!< list of transactions created for
                  MySQL; protected by trx_sys->mutex */
#ifdef UNIV_DEBUG
  bool in_mysql_trx_list;
  /*!< true if in
  trx_sys->mysql_trx_list */
#endif /* UNIV_DEBUG */
  /*------------------------------*/

  /** DB_SUCCESS if no error, otherwise error number.
  Accessed without any mutex only by the thread doing the transaction or, if it
  is suspended (waiting for a lock), by the thread holding this->mutex which
  has changed trx->lock.wait_lock to nullptr and will wake up the transaction.*/
  dberr_t error_state;

  const dict_index_t *error_index; /*!< if the error number indicates a
                                   duplicate key error, a pointer to
                                   the problematic index is stored here */
  ulint error_key_num;             /*!< if the index creation fails to a
                                   duplicate key error, a mysql key
                                   number of that index is stored here */
  sess_t *sess;                    /*!< session of the trx, NULL if none */
  que_t *graph;                    /*!< query currently run in the session,
                                   or NULL if none; NOTE that the query
                                   belongs to the session, and it can
                                   survive over a transaction commit, if
                                   it is a stored procedure with a COMMIT
                                   WORK statement, for instance */
  /*------------------------------*/
  UT_LIST_BASE_NODE_T_EXTERN(trx_named_savept_t, trx_savepoints)
  trx_savepoints{}; /*!< savepoints set with SAVEPOINT ..., oldest first */
  /*------------------------------*/
  UndoMutex undo_mutex; /*!< mutex protecting the fields in this
                        section (down to undo_no_arr), EXCEPT
                        last_sql_stat_start, which can be
                        accessed only when we know that there
                        cannot be any activity in the undo
                        logs! */
  undo_no_t undo_no;    /*!< next undo log record number to
                        assign; since the undo log is
                        private for a transaction, this
                        is a simple ascending sequence
                        with no gaps; thus it represents
                        the number of modified/inserted
                        rows in a transaction */
  space_id_t undo_rseg_space;
  /*!< space id where last undo record
  was written */
  trx_savept_t last_sql_stat_start;
  /*!< undo_no when the last sql statement
  was started: in case of an error, trx
  is rolled back down to this undo
  number; see note at undo_mutex! */
  trx_rsegs_t rsegs;    /* rollback segments for undo logging */
  undo_no_t roll_limit; /*!< least undo number to undo during
                        a partial rollback; 0 otherwise */
#ifdef UNIV_DEBUG
  bool in_rollback;   /*!< true when the transaction is
                      executing a partial or full rollback */
#endif                /* UNIV_DEBUG */
  ulint pages_undone; /*!< number of undo log pages undone
                      since the last undo log truncation */
  /*------------------------------*/
  ulint n_autoinc_rows; /*!< no. of AUTO-INC rows required for
                        an SQL statement. This is useful for
                        multi-row INSERTs */
  /*------------------------------*/
  bool read_only;     /*!< true if transaction is flagged
                      as a READ-ONLY transaction.
                      if auto_commit && will_lock == 0
                      then it will be handled as a
                      AC-NL-RO-SELECT (Auto Commit Non-Locking
                      Read Only Select). A read only
                      transaction will not be assigned an
                      UNDO log. */
  bool auto_commit;   /*!< true if it is an autocommit */
  uint32_t will_lock; /*!< Will acquire some locks. Increment
                         each time we determine that a lock will
                         be acquired by the MySQL layer. */
#ifndef UNIV_HOTBACKUP
  /*------------------------------*/
  fts_trx_t *fts_trx;       /*!< FTS information, or NULL if
                            transaction hasn't modified tables
                            with FTS indexes (yet). */
  doc_id_t fts_next_doc_id; /* The document id used for updates */
  /*------------------------------*/
  uint32_t flush_tables; /*!< if "covering" the FLUSH TABLES",
                            count of tables being flushed. */

  /*------------------------------*/
  bool internal; /*!< true if it is a system/internal
                 transaction background task. Such
                 transactions are always treated as
                 read-write. */
                 /*------------------------------*/
  /** Transaction persists GTID. */
  bool persists_gtid;

#ifdef UNIV_DEBUG
  ulint start_line;       /*!< Track where it was started from */
  const char *start_file; /*!< Filename where it was started */
#endif                    /* UNIV_DEBUG */

  lint n_ref; /*!< Count of references, protected
              by trx_t::mutex. We can't release the
              locks nor commit the transaction until
              this reference is 0.  We can change
              the state to COMMITTED_IN_MEMORY to
              signify that it is no longer
              "active". */

  /** Version of this instance. It is incremented each time the
  instance is re-used in trx_start_low(). It is used to track
  whether a transaction has been restarted since it was tagged
  for asynchronous rollback. */
  std::atomic_uint64_t version;

  XID *xid;                    /*!< X/Open XA transaction
                               identification to identify a
                               transaction branch */
  trx_mod_tables_t mod_tables; /*!< List of tables that were modified
                               by this transaction */
#endif                         /* !UNIV_HOTBACKUP */
                               /*------------------------------*/
  bool api_trx;                /*!< trx started by InnoDB API */
  bool api_auto_commit;        /*!< automatic commit */
  bool read_write;             /*!< if read and write operation */

  /** This flag is set for trx_t objects used by the purge sys. We use the flag
  when validating mysql_trx_list in trx_sys_before_pre_dd_shutdown_validate.
  Purge threads can have allocated trx_t objects visible in the mysql_trx_list
  at this point during shutdown, this is acceptable so we need a way to signal
  this fact. */
  bool purge_sys_trx;
  /*------------------------------*/
  char *detailed_error;           /*!< detailed error message for last
                                  error, or empty. */
  Flush_observer *flush_observer; /*!< flush observer */

#ifdef UNIV_DEBUG
  bool is_dd_trx; /*!< True if the transaction is used for
                  doing Non-locking Read-only Read
                  Committed on DD tables */
#endif            /* UNIV_DEBUG */
  ulint magic_n;

  bool is_read_uncommitted() const {
    return (isolation_level == READ_UNCOMMITTED);
  }

  bool releases_gap_locks_at_prepare() const {
    return isolation_level <= READ_COMMITTED;
  }

  bool skip_gap_locks() const {
    switch (isolation_level) {
      case READ_UNCOMMITTED:
      case READ_COMMITTED:
        return (true);
      case REPEATABLE_READ:
      case SERIALIZABLE:
        return (false);
    }
    ut_d(ut_error);
    ut_o(return (false));
  }

  bool allow_semi_consistent() const { return (skip_gap_locks()); }
  /** Checks if this transaction releases locks on non matching records due to
  low isolation level.
  @return true iff in this transaction's isolation level locks on records which
               do not match the WHERE clause are released */
  bool releases_non_matching_rows() const { return skip_gap_locks(); }
};

#ifndef UNIV_HOTBACKUP
/**
Transactions that aren't started by the MySQL server don't set
the trx_t::mysql_thd field. For such transactions we set the lock
wait timeout to 0 instead of the user configured value that comes
from innodb_lock_wait_timeout via trx_t::mysql_thd.
@param  t transaction
@return lock wait timeout in seconds */
static inline std::chrono::seconds trx_lock_wait_timeout_get(const trx_t *t) {
  return thd_lock_wait_timeout(t->mysql_thd);
}

/**
Determine if the transaction is a non-locking autocommit select
(implied read-only).
@param t transaction
@return true if non-locking autocommit select transaction. */
static inline bool trx_is_autocommit_non_locking(const trx_t *t) {
  return t->auto_commit && t->will_lock == 0;
}

/** Check transaction state */
static inline void check_trx_state(const trx_t *t) {
  ut_ad(!trx_is_autocommit_non_locking(t));
  switch (t->state) {
    case TRX_STATE_PREPARED:
      /* fall through */
    case TRX_STATE_ACTIVE:
    case TRX_STATE_COMMITTED_IN_MEMORY:
      return;
    case TRX_STATE_NOT_STARTED:
    case TRX_STATE_FORCED_ROLLBACK:
      break;
  }
  ut_error;
}

/**
Assert that the transaction is in the trx_sys_t::rw_trx_list */
static inline void assert_trx_in_rw_list(const trx_t *t) {
  ut_ad(!t->read_only);
  ut_ad(t->in_rw_trx_list == !(t->read_only || !t->rsegs.m_redo.rseg));
  check_trx_state(t);
}

/** Check if transaction is free so that it can be re-initialized.
@param t transaction handle */
static inline void assert_trx_is_free(const trx_t *t) {
  ut_ad(trx_state_eq(t, TRX_STATE_NOT_STARTED) ||
        trx_state_eq(t, TRX_STATE_FORCED_ROLLBACK));
  ut_ad(!trx_is_rseg_updated(t));
  ut_ad(!MVCC::is_view_active(t->read_view));
  ut_ad((t)->lock.wait_thr == nullptr);
  ut_ad(UT_LIST_GET_LEN((t)->lock.trx_locks) == 0);
  ut_ad((t)->dict_operation == TRX_DICT_OP_NONE);
}

/** Check if transaction is in-active so that it can be freed and put back to
transaction pool.
@param t transaction handle */
static inline void assert_trx_is_inactive(const trx_t *t) {
  assert_trx_is_free(t);
  ut_ad(t->dict_operation_lock_mode == 0);
}

#ifdef UNIV_DEBUG
/** Assert that an autocommit non-locking select cannot be in the
 rw_trx_list and that it is a read-only transaction.
 The transaction must be in the mysql_trx_list. */
static inline void assert_trx_nonlocking_or_in_list(const trx_t *t) {
  if (trx_is_autocommit_non_locking(t)) {
    trx_state_t t_state = t->state;
    ut_ad(t->read_only);
    ut_ad(!t->is_recovered);
    ut_ad(!t->in_rw_trx_list);
    ut_ad(t->in_mysql_trx_list);
    ut_ad(t_state == TRX_STATE_NOT_STARTED ||
          t_state == TRX_STATE_FORCED_ROLLBACK || t_state == TRX_STATE_ACTIVE);
  } else {
    check_trx_state(t);
  }
}
#else /* UNIV_DEBUG */
/** Assert that an autocommit non-locking select cannot be in the
 rw_trx_list and that it is a read-only transaction.
 The transaction must be in the mysql_trx_list. */
#define assert_trx_nonlocking_or_in_list(trx) ((void)0)
#endif /* UNIV_DEBUG */

/**
Determine if the transaction is a non-locking autocommit select
with an explicit check for the read-only status.
@param t transaction
@return true if non-locking autocommit read-only transaction. */
static inline bool trx_is_ac_nl_ro(const trx_t *t) {
  return t->read_only && trx_is_autocommit_non_locking(t);
}

/**
Increase the reference count. If the transaction is in state
TRX_STATE_COMMITTED_IN_MEMORY then the transaction is considered
committed and the reference count is not incremented.
@param trx Transaction that is being referenced */
static inline void trx_reference(trx_t *trx);

/**
Release the transaction. Decrease the reference count.
@param trx Transaction that is being released */
static inline void trx_release_reference(trx_t *trx);

/**
Check if the transaction is being referenced. */
static inline bool trx_is_referenced(const trx_t *t) { return t->n_ref > 0; }

/** Calculates the "weight" of a transaction. The weight of one transaction
 is estimated as the number of altered rows + the number of locked rows.
 @param t transaction
 @return transaction weight */
static inline uint64_t TRX_WEIGHT(const trx_t *t) {
  return t->undo_no + UT_LIST_GET_LEN(t->lock.trx_locks);
}

#ifdef UNIV_DEBUG
static inline void trx_start_if_not_started_xa(trx_t *t, bool rw,
                                               ut::Location loc) {
  t->start_line = loc.line;
  t->start_file = loc.filename;
  trx_start_if_not_started_xa_low(t, rw);
}

static inline void trx_start_if_not_started(trx_t *t, bool rw, ut::Location l) {
  t->start_line = l.line;
  t->start_file = l.filename;
  trx_start_if_not_started_low(t, rw);
}

static inline void trx_start_internal(trx_t *t, ut::Location loc) {
  t->start_line = loc.line;
  t->start_file = loc.filename;
  trx_start_internal_low(t);
}

static inline void trx_start_internal_read_only(trx_t *t, ut::Location loc) {
  t->start_line = loc.line;
  t->start_file = loc.filename;
  trx_start_internal_read_only_low(t);
}
#else
static inline void trx_start_if_not_started_xa(trx_t *t, bool rw,
                                               ut::Location loc) {
  trx_start_if_not_started_low(t, rw);
}

static inline void trx_start_internal(trx_t *t, ut::Location loc) {
  trx_start_internal_low(t);
}

static inline void trx_start_internal_read_only(trx_t *t, ut::Location loc) {
  trx_start_internal_read_only_low(t);
}

static inline void trx_start_if_not_started(trx_t *t, bool rw, ut::Location l) {
  trx_start_if_not_started_xa_low(t, rw);
}
#endif /* UNIV_DEBUG */

/* Transaction isolation levels (trx->isolation_level) */
#define TRX_ISO_READ_UNCOMMITTED trx_t::READ_UNCOMMITTED
#define TRX_ISO_READ_COMMITTED trx_t::READ_COMMITTED
#define TRX_ISO_REPEATABLE_READ trx_t::REPEATABLE_READ
#define TRX_ISO_SERIALIZABLE trx_t::SERIALIZABLE

/**
Check if transaction was started. Note, that after the check
situation might have already been changed (and note that holding
the trx_sys->mutex does not prevent state transitions for read-only
transactions).
@param[in] trx          Transaction whose state we need to check
@return true if transaction is in state started */
inline bool trx_was_started(const trx_t *trx) {
  const auto trx_state = trx->state.load(std::memory_order_relaxed);
  return trx_state != TRX_STATE_NOT_STARTED &&
         trx_state != TRX_STATE_FORCED_ROLLBACK;
}

/**
Check if transaction is started.
@param[in] trx          Transaction whose state we need to check
@return true if transaction is in state started */
inline bool trx_is_started(const trx_t *trx) {
  ut_ad(trx_can_be_handled_by_current_thread_or_is_hp_victim(trx));
  return trx_was_started(trx);
}

/** Commit node states */
enum commit_node_state {
  COMMIT_NODE_SEND = 1, /*!< about to send a commit signal to
                        the transaction */
  COMMIT_NODE_WAIT      /*!< commit signal sent to the transaction,
                        waiting for completion */
};

/** Commit command node in a query graph */
struct commit_node_t {
  que_common_t common;          /*!< node type: QUE_NODE_COMMIT */
  enum commit_node_state state; /*!< node execution state */
};

#ifdef UNIV_DEBUG

/** Test if trx->mutex is owned by the current thread. */
bool inline trx_mutex_own(const trx_t *trx) { return mutex_own(&trx->mutex); }

/**
Verifies the invariants and records debug state related to latching rules.
Called during trx_mutex_enter before the actual mutex acquisition.
@param[in]  trx             The transaction for which trx_mutex_enter(trx) is
                            called
@param[in]  allow_another   If false, then no further calls to trx_mutex_enter
                            are allowed, until trx_mutex_exit().
                            If true, then this must be the first trx acquisition
                            and we will allow one more.
*/
void trx_before_mutex_enter(const trx_t *trx, bool allow_another);

/**
Verifies the invariants and records debug state related to latching rules.
Called during trx_mutex_exit before the actual mutex release.
@param[in]  trx   The transaction for which trx_mutex_exit(trx) is called
*/
void trx_before_mutex_exit(const trx_t *trx);
#endif

/**
Please do not use this low-level macro.
Use trx_mutex_enter(t) instead.
In rare cases where you need to take two trx->mutex-es, take the first one
using trx_mutex_enter_first_of_two(t1), and the second one with
trx_mutex(2)
*/
#define trx_mutex_enter_low(t, first_of_two)       \
  do {                                             \
    ut_ad(!trx_mutex_own(t));                      \
    ut_d(trx_before_mutex_enter(t, first_of_two)); \
    mutex_enter(&t->mutex);                        \
  } while (0)

/** Acquire the trx->mutex (and promise not to request any more). */
#define trx_mutex_enter(t) trx_mutex_enter_low(t, false)

/** Acquire the trx->mutex (and indicate we might request one more). */
#define trx_mutex_enter_first_of_two(t) trx_mutex_enter_low(t, true)

/** Release the trx->mutex. */
#define trx_mutex_exit(t)           \
  do {                              \
    ut_ad(trx_mutex_own(t));        \
    ut_d(trx_before_mutex_exit(t)); \
    mutex_exit(&t->mutex);          \
  } while (0)

/** Track if a transaction is executing inside InnoDB code. It acts
like a gate between the Server and InnoDB.  */
class TrxInInnoDB {
 public:
  /**
  @param[in,out] trx    Transaction entering InnoDB via the handler
  @param[in] disable    true if called from COMMIT/ROLLBACK method */
  TrxInInnoDB(trx_t *trx, bool disable = false) : m_trx(trx) {
    enter(trx, disable);
  }

  /**
  Destructor */
  ~TrxInInnoDB() { exit(m_trx); }

  /**
  @return true if the transaction has been marked for asynchronous
          rollback */
  bool is_aborted() const { return (is_aborted(m_trx)); }

  /**
  @return true if the transaction can't be rolled back asynchronously */
  bool is_rollback_disabled() const {
    return ((m_trx->in_innodb & TRX_FORCE_ROLLBACK_DISABLE) > 0);
  }

  /**
  @return true if the transaction has been marked for asynchronous
          rollback */
  static bool is_aborted(const trx_t *trx) {
    ut_ad(trx_can_be_handled_by_current_thread_or_is_hp_victim(trx));

    const auto trx_state = trx->state.load(std::memory_order_relaxed);

    if (trx_state == TRX_STATE_NOT_STARTED) {
      return (false);
    }

    ut_ad(srv_read_only_mode || trx->in_depth > 0);
    ut_ad(srv_read_only_mode || trx->in_innodb > 0);

    return (trx->abort || trx_state == TRX_STATE_FORCED_ROLLBACK);
  }

  /**
  Start statement requested for transaction.
  @param[in, out] trx   Transaction at the start of a SQL statement */
  static void begin_stmt(trx_t *trx) { enter(trx, false); }

  /**
  Note an end statement for transaction
  @param[in, out] trx   Transaction at end of a SQL statement */
  static void end_stmt(trx_t *trx) { exit(trx); }

  /**
  @return true if the rollback is being initiated by the thread that
          marked the transaction for asynchronous rollback */
  static bool is_async_rollback(const trx_t *trx) {
    return trx->killed_by == std::this_thread::get_id();
  }

 private:
  /** Note that we have crossed into InnoDB code.
  @param[in]    trx     transaction
  @param[in]    disable true if called from COMMIT/ROLLBACK method */
  static void enter(trx_t *trx, bool disable) {
    if (srv_read_only_mode) {
      return;
    }

    ut_ad(!is_async_rollback(trx));
    ut_ad(trx_can_be_handled_by_current_thread_or_is_hp_victim(trx));

    /* If it hasn't already been marked for async rollback.
    and it will be committed/rolled back. */
    if (disable) {
      trx_mutex_enter(trx);
      if (!is_forced_rollback(trx) && is_started(trx) &&
          !trx_is_autocommit_non_locking(trx)) {
        ut_ad(trx->killed_by == std::thread::id{});

        /* This transaction has crossed the point of
        no return and cannot be rolled back
        asynchronously now. It must commit or rollback
        synchronously. */

        trx->in_innodb |= TRX_FORCE_ROLLBACK_DISABLE;
      }
      trx_mutex_exit(trx);
    }

    /* Avoid excessive mutex acquire/release */
    ++trx->in_depth;

    /* If trx->in_depth is greater than 1 then
    transaction is already in InnoDB. */
    if (trx->in_depth > 1) {
      return;
    }

    trx_mutex_enter(trx);

    wait(trx);

    ut_ad((trx->in_innodb & TRX_FORCE_ROLLBACK_MASK) == 0);

    ++trx->in_innodb;

    trx_mutex_exit(trx);
  }

  /**
  Note that we are exiting InnoDB code */
  static void exit(trx_t *trx) {
    if (srv_read_only_mode) {
      return;
    }

    /* Avoid excessive mutex acquire/release */

    ut_ad(trx->in_depth > 0);

    --trx->in_depth;

    if (trx->in_depth > 0) {
      return;
    }

    trx_mutex_enter(trx);

    ut_ad((trx->in_innodb & TRX_FORCE_ROLLBACK_MASK) > 0);

    --trx->in_innodb;

    trx_mutex_exit(trx);
  }

  /*
  @return true if it is a forced rollback, asynchronously */
  static bool is_forced_rollback(const trx_t *trx) {
    ut_ad(trx_mutex_own(trx));

    return ((trx->in_innodb & TRX_FORCE_ROLLBACK)) > 0;
  }

  /**
  Wait for the asynchronous rollback to complete, if it is in progress */
  static void wait(const trx_t *trx) {
    ut_ad(trx_mutex_own(trx));

    ulint loop_count = 0;
    /* start with optimistic sleep time - 20 micro seconds. */
    ulint sleep_time = 20;

    while (is_forced_rollback(trx)) {
      /* Wait for the async rollback to complete */

      trx_mutex_exit(trx);

      loop_count++;
      /* If the wait is long, don't hog the cpu. */
      if (loop_count < 100) {
        /* 20 microseconds */
        sleep_time = 20;
      } else if (loop_count < 1000) {
        /* 1 millisecond */
        sleep_time = 1000;
      } else {
        /* 100 milliseconds */
        sleep_time = 100000;
      }

      std::this_thread::sleep_for(std::chrono::microseconds(sleep_time));

      trx_mutex_enter(trx);
    }
  }

 private:
  /**
  @return true if transaction is started */
  static bool is_started(const trx_t *trx) {
    ut_ad(trx_mutex_own(trx));

    return trx_is_started(trx);
  }

  /**
  Transaction instance crossing the handler boundary from the Server. */
  trx_t *m_trx;
};

/** Check if transaction is internal XA transaction
@param[in]      trx     transaction
@return true, iff internal XA transaction. */
bool trx_is_mysql_xa(const trx_t *trx);

/** Update transaction binlog file name and position from session THD.
@param[in,out] trx     current transaction. */
void trx_sys_update_binlog_position(trx_t *trx);

/** Checks whether or not the transaction has been marked as prepared in TC.
@param[in]     trx the transaction
@return true if the transaction is marked as prepared in TC, false otherwise. */
bool trx_is_prepared_in_tc(trx_t const *trx);

/** Does the 2nd phase of an XA transaction prepare for MySQL.
@param[in,out] trx Transaction instance to finish prepare
@return DB_SUCCESS or error number */
dberr_t trx_set_prepared_in_tc_for_mysql(trx_t *trx);

#include "trx0trx.ic"
#endif /* !UNIV_HOTBACKUP */

#endif