1
/******************************************************
2
Transaction undo log record
6
Created 3/26/1996 Heikki Tuuri
7
*******************************************************/
16
#include "mach0data.h"
20
#include "dict0dict.h"
24
#include "trx0purge.h"
27
/*=========== UNDO LOG RECORD CREATION AND DECODING ====================*/
29
/**************************************************************************
30
Writes the mtr log entry of the inserted undo log record on the undo log
34
trx_undof_page_add_undo_rec_log(
35
/*============================*/
36
page_t* undo_page, /* in: undo log page */
37
ulint old_free, /* in: start offset of the inserted entry */
38
ulint new_free, /* in: end offset of the entry */
39
mtr_t* mtr) /* in: mtr */
45
log_ptr = mlog_open(mtr, 11 + 13 + MLOG_BUF_MARGIN);
47
if (log_ptr == NULL) {
52
log_end = &log_ptr[11 + 13 + MLOG_BUF_MARGIN];
53
log_ptr = mlog_write_initial_log_record_fast(
54
undo_page, MLOG_UNDO_INSERT, log_ptr, mtr);
55
len = new_free - old_free - 4;
57
mach_write_to_2(log_ptr, len);
60
if (log_ptr + len <= log_end) {
61
memcpy(log_ptr, undo_page + old_free + 2, len);
62
mlog_close(mtr, log_ptr + len);
64
mlog_close(mtr, log_ptr);
65
mlog_catenate_string(mtr, undo_page + old_free + 2, len);
69
/***************************************************************
70
Parses a redo log record of adding an undo log record. */
73
trx_undo_parse_add_undo_rec(
74
/*========================*/
75
/* out: end of log record or NULL */
76
byte* ptr, /* in: buffer */
77
byte* end_ptr,/* in: buffer end */
78
page_t* page) /* in: page or NULL */
84
if (end_ptr < ptr + 2) {
89
len = mach_read_from_2(ptr);
92
if (end_ptr < ptr + len) {
102
first_free = mach_read_from_2(page + TRX_UNDO_PAGE_HDR
103
+ TRX_UNDO_PAGE_FREE);
104
rec = page + first_free;
106
mach_write_to_2(rec, first_free + 4 + len);
107
mach_write_to_2(rec + 2 + len, first_free);
109
mach_write_to_2(page + TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_FREE,
110
first_free + 4 + len);
111
ut_memcpy(rec + 2, ptr, len);
116
/**************************************************************************
117
Calculates the free space left for extending an undo log record. */
122
/* out: bytes left */
123
page_t* page, /* in: undo log page */
124
byte* ptr) /* in: pointer to page */
126
/* The '- 10' is a safety margin, in case we have some small
127
calculation error below */
129
return(UNIV_PAGE_SIZE - (ptr - page) - 10 - FIL_PAGE_DATA_END);
132
/**************************************************************************
133
Reports in the undo log of an insert of a clustered index record. */
136
trx_undo_page_report_insert(
137
/*========================*/
138
/* out: offset of the inserted entry
139
on the page if succeed, 0 if fail */
140
page_t* undo_page, /* in: undo log page */
141
trx_t* trx, /* in: transaction */
142
dict_index_t* index, /* in: clustered index */
143
dtuple_t* clust_entry, /* in: index entry which will be
144
inserted to the clustered index */
145
mtr_t* mtr) /* in: mtr */
154
ut_ad(mach_read_from_2(undo_page + TRX_UNDO_PAGE_HDR
155
+ TRX_UNDO_PAGE_TYPE) == TRX_UNDO_INSERT);
157
first_free = mach_read_from_2(undo_page + TRX_UNDO_PAGE_HDR
158
+ TRX_UNDO_PAGE_FREE);
159
ptr = undo_page + first_free;
161
ut_ad(first_free <= UNIV_PAGE_SIZE);
163
if (trx_undo_left(undo_page, ptr) < 30) {
165
/* NOTE: the value 30 must be big enough such that the general
166
fields written below fit on the undo log page */
171
/* Reserve 2 bytes for the pointer to the next undo log record */
174
/* Store first some general parameters to the undo log */
175
mach_write_to_1(ptr, TRX_UNDO_INSERT_REC);
178
len = mach_dulint_write_much_compressed(ptr, trx->undo_no);
181
len = mach_dulint_write_much_compressed(ptr, (index->table)->id);
183
/*----------------------------------------*/
184
/* Store then the fields required to uniquely determine the record
185
to be inserted in the clustered index */
187
for (i = 0; i < dict_index_get_n_unique(index); i++) {
189
field = dtuple_get_nth_field(clust_entry, i);
191
flen = dfield_get_len(field);
193
if (trx_undo_left(undo_page, ptr) < 5) {
198
len = mach_write_compressed(ptr, flen);
201
if (flen != UNIV_SQL_NULL) {
202
if (trx_undo_left(undo_page, ptr) < flen) {
207
ut_memcpy(ptr, dfield_get_data(field), flen);
212
if (trx_undo_left(undo_page, ptr) < 2) {
217
/*----------------------------------------*/
218
/* Write pointers to the previous and the next undo log records */
220
if (trx_undo_left(undo_page, ptr) < 2) {
225
mach_write_to_2(ptr, first_free);
228
mach_write_to_2(undo_page + first_free, ptr - undo_page);
230
mach_write_to_2(undo_page + TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_FREE,
233
/* Write the log entry to the REDO log of this change in the UNDO
235
trx_undof_page_add_undo_rec_log(undo_page, first_free,
236
ptr - undo_page, mtr);
240
/**************************************************************************
241
Reads from an undo log record the general parameters. */
244
trx_undo_rec_get_pars(
245
/*==================*/
246
/* out: remaining part of undo log
247
record after reading these values */
248
trx_undo_rec_t* undo_rec, /* in: undo log record */
249
ulint* type, /* out: undo record type:
250
TRX_UNDO_INSERT_REC, ... */
251
ulint* cmpl_info, /* out: compiler info, relevant only
252
for update type records */
253
ibool* updated_extern, /* out: TRUE if we updated an
254
externally stored fild */
255
dulint* undo_no, /* out: undo log record number */
256
dulint* table_id) /* out: table id */
264
type_cmpl = mach_read_from_1(ptr);
267
if (type_cmpl & TRX_UNDO_UPD_EXTERN) {
268
*updated_extern = TRUE;
269
type_cmpl -= TRX_UNDO_UPD_EXTERN;
271
*updated_extern = FALSE;
274
*type = type_cmpl & (TRX_UNDO_CMPL_INFO_MULT - 1);
275
*cmpl_info = type_cmpl / TRX_UNDO_CMPL_INFO_MULT;
277
*undo_no = mach_dulint_read_much_compressed(ptr);
278
len = mach_dulint_get_much_compressed_size(*undo_no);
281
*table_id = mach_dulint_read_much_compressed(ptr);
282
len = mach_dulint_get_much_compressed_size(*table_id);
288
/**************************************************************************
289
Reads from an undo log record a stored column value. */
292
trx_undo_rec_get_col_val(
293
/*=====================*/
294
/* out: remaining part of undo log record after
295
reading these values */
296
byte* ptr, /* in: pointer to remaining part of undo log record */
297
byte** field, /* out: pointer to stored field */
298
ulint* len) /* out: length of the field, or UNIV_SQL_NULL */
300
*len = mach_read_compressed(ptr);
301
ptr += mach_get_compressed_size(*len);
305
if (*len != UNIV_SQL_NULL) {
306
if (*len >= UNIV_EXTERN_STORAGE_FIELD) {
307
ptr += (*len - UNIV_EXTERN_STORAGE_FIELD);
316
/***********************************************************************
317
Builds a row reference from an undo log record. */
320
trx_undo_rec_get_row_ref(
321
/*=====================*/
322
/* out: pointer to remaining part of undo
324
byte* ptr, /* in: remaining part of a copy of an undo log
325
record, at the start of the row reference;
326
NOTE that this copy of the undo log record must
327
be preserved as long as the row reference is
328
used, as we do NOT copy the data in the
330
dict_index_t* index, /* in: clustered index */
331
dtuple_t** ref, /* out, own: row reference */
332
mem_heap_t* heap) /* in: memory heap from which the memory
333
needed is allocated */
341
ut_ad(index && ptr && ref && heap);
342
ut_a(index->type & DICT_CLUSTERED);
344
ref_len = dict_index_get_n_unique(index);
346
*ref = dtuple_create(heap, ref_len);
348
dict_index_copy_types(*ref, index, ref_len);
350
for (i = 0; i < ref_len; i++) {
351
dfield = dtuple_get_nth_field(*ref, i);
353
ptr = trx_undo_rec_get_col_val(ptr, &field, &len);
355
dfield_set_data(dfield, field, len);
361
/***********************************************************************
362
Skips a row reference from an undo log record. */
365
trx_undo_rec_skip_row_ref(
366
/*======================*/
367
/* out: pointer to remaining part of undo
369
byte* ptr, /* in: remaining part in update undo log
370
record, at the start of the row reference */
371
dict_index_t* index) /* in: clustered index */
379
ut_a(index->type & DICT_CLUSTERED);
381
ref_len = dict_index_get_n_unique(index);
383
for (i = 0; i < ref_len; i++) {
384
ptr = trx_undo_rec_get_col_val(ptr, &field, &len);
390
/**************************************************************************
391
Reports in the undo log of an update or delete marking of a clustered index
395
trx_undo_page_report_modify(
396
/*========================*/
397
/* out: byte offset of the inserted
398
undo log entry on the page if succeed,
400
page_t* undo_page, /* in: undo log page */
401
trx_t* trx, /* in: transaction */
402
dict_index_t* index, /* in: clustered index where update or
403
delete marking is done */
404
rec_t* rec, /* in: clustered index record which
405
has NOT yet been modified */
406
const ulint* offsets, /* in: rec_get_offsets(rec, index) */
407
upd_t* update, /* in: update vector which tells the
408
columns to be updated; in the case of
409
a delete, this should be set to NULL */
410
ulint cmpl_info, /* in: compiler info on secondary
412
mtr_t* mtr) /* in: mtr */
415
upd_field_t* upd_field;
431
ut_a(index->type & DICT_CLUSTERED);
432
ut_ad(rec_offs_validate(rec, index, offsets));
433
ut_ad(mach_read_from_2(undo_page + TRX_UNDO_PAGE_HDR
434
+ TRX_UNDO_PAGE_TYPE) == TRX_UNDO_UPDATE);
435
table = index->table;
437
first_free = mach_read_from_2(undo_page + TRX_UNDO_PAGE_HDR
438
+ TRX_UNDO_PAGE_FREE);
439
ptr = undo_page + first_free;
441
ut_ad(first_free <= UNIV_PAGE_SIZE);
443
if (trx_undo_left(undo_page, ptr) < 50) {
445
/* NOTE: the value 50 must be big enough so that the general
446
fields written below fit on the undo log page */
451
/* Reserve 2 bytes for the pointer to the next undo log record */
454
/* Store first some general parameters to the undo log */
457
if (rec_get_deleted_flag(rec, dict_table_is_comp(table))) {
458
type_cmpl = TRX_UNDO_UPD_DEL_REC;
460
type_cmpl = TRX_UNDO_UPD_EXIST_REC;
463
type_cmpl = TRX_UNDO_DEL_MARK_REC;
466
type_cmpl = type_cmpl | (cmpl_info * TRX_UNDO_CMPL_INFO_MULT);
468
mach_write_to_1(ptr, type_cmpl);
473
len = mach_dulint_write_much_compressed(ptr, trx->undo_no);
476
len = mach_dulint_write_much_compressed(ptr, table->id);
479
/*----------------------------------------*/
480
/* Store the state of the info bits */
482
bits = rec_get_info_bits(rec, dict_table_is_comp(table));
483
mach_write_to_1(ptr, bits);
486
/* Store the values of the system columns */
487
field = rec_get_nth_field(rec, offsets,
488
dict_index_get_sys_col_pos(
489
index, DATA_TRX_ID), &len);
490
ut_ad(len == DATA_TRX_ID_LEN);
491
trx_id = trx_read_trx_id(field);
492
field = rec_get_nth_field(rec, offsets,
493
dict_index_get_sys_col_pos(
494
index, DATA_ROLL_PTR), &len);
495
ut_ad(len == DATA_ROLL_PTR_LEN);
496
roll_ptr = trx_read_roll_ptr(field);
498
len = mach_dulint_write_compressed(ptr, trx_id);
501
len = mach_dulint_write_compressed(ptr, roll_ptr);
504
/*----------------------------------------*/
505
/* Store then the fields required to uniquely determine the
506
record which will be modified in the clustered index */
508
for (i = 0; i < dict_index_get_n_unique(index); i++) {
510
field = rec_get_nth_field(rec, offsets, i, &flen);
512
if (trx_undo_left(undo_page, ptr) < 4) {
517
len = mach_write_compressed(ptr, flen);
520
if (flen != UNIV_SQL_NULL) {
521
if (trx_undo_left(undo_page, ptr) < flen) {
526
ut_memcpy(ptr, field, flen);
531
/*----------------------------------------*/
532
/* Save to the undo log the old values of the columns to be updated. */
535
if (trx_undo_left(undo_page, ptr) < 5) {
540
len = mach_write_compressed(ptr, upd_get_n_fields(update));
543
for (i = 0; i < upd_get_n_fields(update); i++) {
545
upd_field = upd_get_nth_field(update, i);
546
pos = upd_field->field_no;
548
/* Write field number to undo log */
549
if (trx_undo_left(undo_page, ptr) < 5) {
554
len = mach_write_compressed(ptr, pos);
557
/* Save the old value of field */
558
field = rec_get_nth_field(rec, offsets, pos, &flen);
560
if (trx_undo_left(undo_page, ptr) < 5) {
565
if (rec_offs_nth_extern(offsets, pos)) {
566
/* If a field has external storage, we add
569
len = mach_write_compressed(
571
UNIV_EXTERN_STORAGE_FIELD + flen);
573
/* Notify purge that it eventually has to
574
free the old externally stored field */
576
trx->update_undo->del_marks = TRUE;
578
*type_cmpl_ptr = *type_cmpl_ptr
579
| TRX_UNDO_UPD_EXTERN;
581
len = mach_write_compressed(ptr, flen);
586
if (flen != UNIV_SQL_NULL) {
587
if (trx_undo_left(undo_page, ptr) < flen) {
592
ut_memcpy(ptr, field, flen);
598
/*----------------------------------------*/
599
/* In the case of a delete marking, and also in the case of an update
600
where any ordering field of any index changes, store the values of all
601
columns which occur as ordering fields in any index. This info is used
602
in the purge of old versions where we use it to build and search the
603
delete marked index records, to look if we can remove them from the
604
index tree. Note that starting from 4.0.14 also externally stored
605
fields can be ordering in some index. But we always store at least
606
384 first bytes locally to the clustered index record, which means
607
we can construct the column prefix fields in the index from the
610
if (!update || !(cmpl_info & UPD_NODE_NO_ORD_CHANGE)) {
612
trx->update_undo->del_marks = TRUE;
614
if (trx_undo_left(undo_page, ptr) < 5) {
621
/* Reserve 2 bytes to write the number of bytes the stored
622
fields take in this undo record */
626
for (col_no = 0; col_no < dict_table_get_n_cols(table);
629
const dict_col_t* col
630
= dict_table_get_nth_col(table, col_no);
632
if (col->ord_part > 0) {
634
pos = dict_index_get_nth_col_pos(index,
637
/* Write field number to undo log */
638
if (trx_undo_left(undo_page, ptr) < 5) {
643
len = mach_write_compressed(ptr, pos);
646
/* Save the old value of field */
647
field = rec_get_nth_field(rec, offsets, pos,
650
if (trx_undo_left(undo_page, ptr) < 5) {
655
len = mach_write_compressed(ptr, flen);
658
if (flen != UNIV_SQL_NULL) {
659
if (trx_undo_left(undo_page, ptr)
665
ut_memcpy(ptr, field, flen);
671
mach_write_to_2(old_ptr, ptr - old_ptr);
674
/*----------------------------------------*/
675
/* Write pointers to the previous and the next undo log records */
676
if (trx_undo_left(undo_page, ptr) < 2) {
681
mach_write_to_2(ptr, first_free);
683
mach_write_to_2(undo_page + first_free, ptr - undo_page);
685
mach_write_to_2(undo_page + TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_FREE,
688
/* Write to the REDO log about this change in the UNDO log */
690
trx_undof_page_add_undo_rec_log(undo_page, first_free,
691
ptr - undo_page, mtr);
695
/**************************************************************************
696
Reads from an undo log update record the system field values of the old
700
trx_undo_update_rec_get_sys_cols(
701
/*=============================*/
702
/* out: remaining part of undo log
703
record after reading these values */
704
byte* ptr, /* in: remaining part of undo log
705
record after reading general
707
dulint* trx_id, /* out: trx id */
708
dulint* roll_ptr, /* out: roll ptr */
709
ulint* info_bits) /* out: info bits state */
713
/* Read the state of the info bits */
714
*info_bits = mach_read_from_1(ptr);
717
/* Read the values of the system columns */
719
*trx_id = mach_dulint_read_compressed(ptr);
720
len = mach_dulint_get_compressed_size(*trx_id);
723
*roll_ptr = mach_dulint_read_compressed(ptr);
724
len = mach_dulint_get_compressed_size(*roll_ptr);
730
/**************************************************************************
731
Reads from an update undo log record the number of updated fields. */
734
trx_undo_update_rec_get_n_upd_fields(
735
/*=================================*/
736
/* out: remaining part of undo log record after
737
reading this value */
738
byte* ptr, /* in: pointer to remaining part of undo log record */
739
ulint* n) /* out: number of fields */
741
*n = mach_read_compressed(ptr);
742
ptr += mach_get_compressed_size(*n);
747
/**************************************************************************
748
Reads from an update undo log record a stored field number. */
751
trx_undo_update_rec_get_field_no(
752
/*=============================*/
753
/* out: remaining part of undo log record after
754
reading this value */
755
byte* ptr, /* in: pointer to remaining part of undo log record */
756
ulint* field_no)/* out: field number */
758
*field_no = mach_read_compressed(ptr);
759
ptr += mach_get_compressed_size(*field_no);
764
/***********************************************************************
765
Builds an update vector based on a remaining part of an undo log record. */
768
trx_undo_update_rec_get_update(
769
/*===========================*/
770
/* out: remaining part of the record,
771
NULL if an error detected, which means that
772
the record is corrupted */
773
byte* ptr, /* in: remaining part in update undo log
774
record, after reading the row reference
775
NOTE that this copy of the undo log record must
776
be preserved as long as the update vector is
777
used, as we do NOT copy the data in the
779
dict_index_t* index, /* in: clustered index */
780
ulint type, /* in: TRX_UNDO_UPD_EXIST_REC,
781
TRX_UNDO_UPD_DEL_REC, or
782
TRX_UNDO_DEL_MARK_REC; in the last case,
783
only trx id and roll ptr fields are added to
785
dulint trx_id, /* in: transaction id from this undo record */
786
dulint roll_ptr,/* in: roll pointer from this undo record */
787
ulint info_bits,/* in: info bits from this undo record */
788
trx_t* trx, /* in: transaction */
789
mem_heap_t* heap, /* in: memory heap from which the memory
790
needed is allocated */
791
upd_t** upd) /* out, own: update vector */
793
upd_field_t* upd_field;
802
ut_a(index->type & DICT_CLUSTERED);
804
if (type != TRX_UNDO_DEL_MARK_REC) {
805
ptr = trx_undo_update_rec_get_n_upd_fields(ptr, &n_fields);
810
update = upd_create(n_fields + 2, heap);
812
update->info_bits = info_bits;
814
/* Store first trx id and roll ptr to update vector */
816
upd_field = upd_get_nth_field(update, n_fields);
817
buf = mem_heap_alloc(heap, DATA_TRX_ID_LEN);
818
trx_write_trx_id(buf, trx_id);
820
upd_field_set_field_no(upd_field,
821
dict_index_get_sys_col_pos(index, DATA_TRX_ID),
823
dfield_set_data(&(upd_field->new_val), buf, DATA_TRX_ID_LEN);
825
upd_field = upd_get_nth_field(update, n_fields + 1);
826
buf = mem_heap_alloc(heap, DATA_ROLL_PTR_LEN);
827
trx_write_roll_ptr(buf, roll_ptr);
829
upd_field_set_field_no(
830
upd_field, dict_index_get_sys_col_pos(index, DATA_ROLL_PTR),
832
dfield_set_data(&(upd_field->new_val), buf, DATA_ROLL_PTR_LEN);
834
/* Store then the updated ordinary columns to the update vector */
836
for (i = 0; i < n_fields; i++) {
838
ptr = trx_undo_update_rec_get_field_no(ptr, &field_no);
840
if (field_no >= dict_index_get_n_fields(index)) {
842
"InnoDB: Error: trying to access"
843
" update undo rec field %lu in ",
845
dict_index_name_print(stderr, trx, index);
847
"InnoDB: but index has only %lu fields\n"
848
"InnoDB: Submit a detailed bug report"
849
" to http://bugs.mysql.com\n"
850
"InnoDB: Run also CHECK TABLE ",
851
(ulong) dict_index_get_n_fields(index));
852
ut_print_name(stderr, trx, TRUE, index->table_name);
854
"InnoDB: n_fields = %lu, i = %lu, ptr %p\n",
855
(ulong) n_fields, (ulong) i, ptr);
859
ptr = trx_undo_rec_get_col_val(ptr, &field, &len);
861
upd_field = upd_get_nth_field(update, i);
863
upd_field_set_field_no(upd_field, field_no, index, trx);
865
if (len != UNIV_SQL_NULL && len >= UNIV_EXTERN_STORAGE_FIELD) {
867
upd_field->extern_storage = TRUE;
869
len -= UNIV_EXTERN_STORAGE_FIELD;
872
dfield_set_data(&(upd_field->new_val), field, len);
880
/***********************************************************************
881
Builds a partial row from an update undo log record. It contains the
882
columns which occur as ordering in any index of the table. */
885
trx_undo_rec_get_partial_row(
886
/*=========================*/
887
/* out: pointer to remaining part of undo
889
byte* ptr, /* in: remaining part in update undo log
890
record of a suitable type, at the start of
891
the stored index columns;
892
NOTE that this copy of the undo log record must
893
be preserved as long as the partial row is
894
used, as we do NOT copy the data in the
896
dict_index_t* index, /* in: clustered index */
897
dtuple_t** row, /* out, own: partial row */
898
mem_heap_t* heap) /* in: memory heap from which the memory
899
needed is allocated */
911
ut_ad(index && ptr && row && heap);
913
row_len = dict_table_get_n_cols(index->table);
915
*row = dtuple_create(heap, row_len);
917
dict_table_copy_types(*row, index->table);
921
total_len = mach_read_from_2(ptr);
926
if (ptr == start_ptr + total_len) {
931
ptr = trx_undo_update_rec_get_field_no(ptr, &field_no);
933
col_no = dict_index_get_nth_col_no(index, field_no);
935
ptr = trx_undo_rec_get_col_val(ptr, &field, &len);
937
dfield = dtuple_get_nth_field(*row, col_no);
939
dfield_set_data(dfield, field, len);
945
/***************************************************************************
946
Erases the unused undo log page end. */
949
trx_undo_erase_page_end(
950
/*====================*/
951
page_t* undo_page, /* in: undo page whose end to erase */
952
mtr_t* mtr) /* in: mtr */
956
first_free = mach_read_from_2(undo_page + TRX_UNDO_PAGE_HDR
957
+ TRX_UNDO_PAGE_FREE);
958
memset(undo_page + first_free, 0xff,
959
(UNIV_PAGE_SIZE - FIL_PAGE_DATA_END) - first_free);
961
mlog_write_initial_log_record(undo_page, MLOG_UNDO_ERASE_END, mtr);
964
/***************************************************************
965
Parses a redo log record of erasing of an undo page end. */
968
trx_undo_parse_erase_page_end(
969
/*==========================*/
970
/* out: end of log record or NULL */
971
byte* ptr, /* in: buffer */
972
byte* end_ptr __attribute__((unused)), /* in: buffer end */
973
page_t* page, /* in: page or NULL */
974
mtr_t* mtr) /* in: mtr or NULL */
976
ut_ad(ptr && end_ptr);
983
trx_undo_erase_page_end(page, mtr);
988
/***************************************************************************
989
Writes information to an undo log about an insert, update, or a delete marking
990
of a clustered index record. This information is used in a rollback of the
991
transaction and in consistent reads that must look to the history of this
995
trx_undo_report_row_operation(
996
/*==========================*/
997
/* out: DB_SUCCESS or error code */
998
ulint flags, /* in: if BTR_NO_UNDO_LOG_FLAG bit is
1000
ulint op_type, /* in: TRX_UNDO_INSERT_OP or
1001
TRX_UNDO_MODIFY_OP */
1002
que_thr_t* thr, /* in: query thread */
1003
dict_index_t* index, /* in: clustered index */
1004
dtuple_t* clust_entry, /* in: in the case of an insert,
1005
index entry to insert into the
1006
clustered index, otherwise NULL */
1007
upd_t* update, /* in: in the case of an update,
1008
the update vector, otherwise NULL */
1009
ulint cmpl_info, /* in: compiler info on secondary
1011
rec_t* rec, /* in: in case of an update or delete
1012
marking, the record in the clustered
1013
index, otherwise NULL */
1014
dulint* roll_ptr) /* out: rollback pointer to the
1015
inserted undo log record,
1016
ut_dulint_zero if BTR_NO_UNDO_LOG
1017
flag was specified */
1027
ulint err = DB_SUCCESS;
1028
mem_heap_t* heap = NULL;
1029
ulint offsets_[REC_OFFS_NORMAL_SIZE];
1030
ulint* offsets = offsets_;
1031
*offsets_ = (sizeof offsets_) / sizeof *offsets_;
1033
ut_a(index->type & DICT_CLUSTERED);
1035
if (flags & BTR_NO_UNDO_LOG_FLAG) {
1037
*roll_ptr = ut_dulint_zero;
1043
ut_ad((op_type != TRX_UNDO_INSERT_OP)
1044
|| (clust_entry && !update && !rec));
1046
trx = thr_get_trx(thr);
1049
mutex_enter(&(trx->undo_mutex));
1051
/* If the undo log is not assigned yet, assign one */
1053
if (op_type == TRX_UNDO_INSERT_OP) {
1055
if (trx->insert_undo == NULL) {
1057
err = trx_undo_assign_undo(trx, TRX_UNDO_INSERT);
1060
undo = trx->insert_undo;
1063
ut_ad(op_type == TRX_UNDO_MODIFY_OP);
1065
if (trx->update_undo == NULL) {
1067
err = trx_undo_assign_undo(trx, TRX_UNDO_UPDATE);
1071
undo = trx->update_undo;
1075
if (err != DB_SUCCESS) {
1076
/* Did not succeed: return the error encountered */
1077
mutex_exit(&(trx->undo_mutex));
1082
page_no = undo->last_page_no;
1087
undo_page = buf_page_get_gen(undo->space, page_no,
1088
RW_X_LATCH, undo->guess_page,
1093
#ifdef UNIV_SYNC_DEBUG
1094
buf_page_dbg_add_level(undo_page, SYNC_TRX_UNDO_PAGE);
1095
#endif /* UNIV_SYNC_DEBUG */
1097
if (op_type == TRX_UNDO_INSERT_OP) {
1098
offset = trx_undo_page_report_insert(
1099
undo_page, trx, index, clust_entry, &mtr);
1101
offsets = rec_get_offsets(rec, index, offsets,
1102
ULINT_UNDEFINED, &heap);
1103
offset = trx_undo_page_report_modify(
1104
undo_page, trx, index, rec, offsets, update,
1109
/* The record did not fit on the page. We erase the
1110
end segment of the undo log page and write a log
1111
record of it: this is to ensure that in the debug
1112
version the replicate page constructed using the log
1113
records stays identical to the original page */
1115
trx_undo_erase_page_end(undo_page, &mtr);
1126
ut_ad(page_no == undo->last_page_no);
1128
/* We have to extend the undo log by one page */
1132
/* When we add a page to an undo log, this is analogous to
1133
a pessimistic insert in a B-tree, and we must reserve the
1134
counterpart of the tree latch, which is the rseg mutex. */
1136
mutex_enter(&(rseg->mutex));
1138
page_no = trx_undo_add_page(trx, undo, &mtr);
1140
mutex_exit(&(rseg->mutex));
1142
if (page_no == FIL_NULL) {
1143
/* Did not succeed: out of space */
1145
mutex_exit(&(trx->undo_mutex));
1147
if (UNIV_LIKELY_NULL(heap)) {
1148
mem_heap_free(heap);
1150
return(DB_OUT_OF_FILE_SPACE);
1154
undo->empty = FALSE;
1155
undo->top_page_no = page_no;
1156
undo->top_offset = offset;
1157
undo->top_undo_no = trx->undo_no;
1158
undo->guess_page = undo_page;
1160
UT_DULINT_INC(trx->undo_no);
1162
mutex_exit(&(trx->undo_mutex));
1164
*roll_ptr = trx_undo_build_roll_ptr(is_insert, rseg->id, page_no,
1166
if (UNIV_LIKELY_NULL(heap)) {
1167
mem_heap_free(heap);
1172
/*============== BUILDING PREVIOUS VERSION OF A RECORD ===============*/
1174
/**********************************************************************
1175
Copies an undo record to heap. This function can be called if we know that
1176
the undo log record exists. */
1179
trx_undo_get_undo_rec_low(
1180
/*======================*/
1181
/* out, own: copy of the record */
1182
dulint roll_ptr, /* in: roll pointer to record */
1183
mem_heap_t* heap) /* in: memory heap where copied */
1185
trx_undo_rec_t* undo_rec;
1194
trx_undo_decode_roll_ptr(roll_ptr, &is_insert, &rseg_id, &page_no,
1196
rseg = trx_rseg_get_on_id(rseg_id);
1200
undo_page = trx_undo_page_get_s_latched(rseg->space, page_no, &mtr);
1202
undo_rec = trx_undo_rec_copy(undo_page + offset, heap);
1209
/**********************************************************************
1210
Copies an undo record to heap. */
1213
trx_undo_get_undo_rec(
1214
/*==================*/
1215
/* out: DB_SUCCESS, or
1216
DB_MISSING_HISTORY if the undo log
1217
has been truncated and we cannot
1218
fetch the old version; NOTE: the
1219
caller must have latches on the
1220
clustered index page and purge_view */
1221
dulint roll_ptr, /* in: roll pointer to record */
1222
dulint trx_id, /* in: id of the trx that generated
1223
the roll pointer: it points to an
1224
undo log of this transaction */
1225
trx_undo_rec_t** undo_rec, /* out, own: copy of the record */
1226
mem_heap_t* heap) /* in: memory heap where copied */
1228
#ifdef UNIV_SYNC_DEBUG
1229
ut_ad(rw_lock_own(&(purge_sys->latch), RW_LOCK_SHARED));
1230
#endif /* UNIV_SYNC_DEBUG */
1232
if (!trx_purge_update_undo_must_exist(trx_id)) {
1234
/* It may be that the necessary undo log has already been
1237
return(DB_MISSING_HISTORY);
1240
*undo_rec = trx_undo_get_undo_rec_low(roll_ptr, heap);
1245
/***********************************************************************
1246
Build a previous version of a clustered index record. This function checks
1247
that the caller has a latch on the index page of the clustered index record
1248
and an s-latch on the purge_view. This guarantees that the stack of versions
1252
trx_undo_prev_version_build(
1253
/*========================*/
1254
/* out: DB_SUCCESS, or DB_MISSING_HISTORY if
1255
the previous version is not >= purge_view,
1256
which means that it may have been removed,
1257
DB_ERROR if corrupted record */
1258
rec_t* index_rec,/* in: clustered index record in the
1260
mtr_t* index_mtr __attribute__((unused)),
1261
/* in: mtr which contains the latch to
1262
index_rec page and purge_view */
1263
rec_t* rec, /* in: version of a clustered index record */
1264
dict_index_t* index, /* in: clustered index */
1265
ulint* offsets,/* in: rec_get_offsets(rec, index) */
1266
mem_heap_t* heap, /* in: memory heap from which the memory
1267
needed is allocated */
1268
rec_t** old_vers)/* out, own: previous version, or NULL if
1269
rec is the first inserted version, or if
1270
history data has been deleted */
1272
trx_undo_rec_t* undo_rec;
1280
dulint old_roll_ptr;
1288
#ifdef UNIV_SYNC_DEBUG
1289
ut_ad(rw_lock_own(&(purge_sys->latch), RW_LOCK_SHARED));
1290
#endif /* UNIV_SYNC_DEBUG */
1291
ut_ad(mtr_memo_contains(index_mtr, buf_block_align(index_rec),
1292
MTR_MEMO_PAGE_S_FIX)
1293
|| mtr_memo_contains(index_mtr, buf_block_align(index_rec),
1294
MTR_MEMO_PAGE_X_FIX));
1295
ut_ad(rec_offs_validate(rec, index, offsets));
1297
if (!(index->type & DICT_CLUSTERED)) {
1298
fprintf(stderr, "InnoDB: Error: trying to access"
1299
" update undo rec for non-clustered index %s\n"
1300
"InnoDB: Submit a detailed bug report to"
1301
" http://bugs.mysql.com\n"
1302
"InnoDB: index record ", index->name);
1303
rec_print(stderr, index_rec, index);
1305
"InnoDB: record version ", stderr);
1306
rec_print_new(stderr, rec, offsets);
1311
roll_ptr = row_get_rec_roll_ptr(rec, index, offsets);
1312
old_roll_ptr = roll_ptr;
1316
if (trx_undo_roll_ptr_is_insert(roll_ptr)) {
1318
/* The record rec is the first inserted version */
1323
rec_trx_id = row_get_rec_trx_id(rec, index, offsets);
1325
err = trx_undo_get_undo_rec(roll_ptr, rec_trx_id, &undo_rec, heap);
1327
if (err != DB_SUCCESS) {
1332
ptr = trx_undo_rec_get_pars(undo_rec, &type, &cmpl_info,
1333
&dummy_extern, &undo_no, &table_id);
1335
ptr = trx_undo_update_rec_get_sys_cols(ptr, &trx_id, &roll_ptr,
1337
ptr = trx_undo_rec_skip_row_ref(ptr, index);
1339
ptr = trx_undo_update_rec_get_update(ptr, index, type, trx_id,
1340
roll_ptr, info_bits,
1341
NULL, heap, &update);
1343
if (ut_dulint_cmp(table_id, index->table->id) != 0) {
1347
"InnoDB: Error: trying to access update undo rec"
1349
"InnoDB: but the table id in the"
1350
" undo record is wrong\n"
1351
"InnoDB: Submit a detailed bug report"
1352
" to http://bugs.mysql.com\n"
1353
"InnoDB: Run also CHECK TABLE %s\n",
1354
index->table_name, index->table_name);
1358
/* The record was corrupted, return an error; these printfs
1359
should catch an elusive bug in row_vers_old_has_index_entry */
1362
"InnoDB: table %s, index %s, n_uniq %lu\n"
1363
"InnoDB: undo rec address %p, type %lu cmpl_info %lu\n"
1364
"InnoDB: undo rec table id %lu %lu,"
1365
" index table id %lu %lu\n"
1366
"InnoDB: dump of 150 bytes in undo rec: ",
1367
index->table_name, index->name,
1368
(ulong) dict_index_get_n_unique(index),
1369
undo_rec, (ulong) type, (ulong) cmpl_info,
1370
(ulong) ut_dulint_get_high(table_id),
1371
(ulong) ut_dulint_get_low(table_id),
1372
(ulong) ut_dulint_get_high(index->table->id),
1373
(ulong) ut_dulint_get_low(index->table->id));
1374
ut_print_buf(stderr, undo_rec, 150);
1376
"InnoDB: index record ", stderr);
1377
rec_print(stderr, index_rec, index);
1379
"InnoDB: record version ", stderr);
1380
rec_print_new(stderr, rec, offsets);
1381
fprintf(stderr, "\n"
1382
"InnoDB: Record trx id %lu %lu, update rec"
1384
"InnoDB: Roll ptr in rec %lu %lu, in update rec"
1386
(ulong) ut_dulint_get_high(rec_trx_id),
1387
(ulong) ut_dulint_get_low(rec_trx_id),
1388
(ulong) ut_dulint_get_high(trx_id),
1389
(ulong) ut_dulint_get_low(trx_id),
1390
(ulong) ut_dulint_get_high(old_roll_ptr),
1391
(ulong) ut_dulint_get_low(old_roll_ptr),
1392
(ulong) ut_dulint_get_high(roll_ptr),
1393
(ulong) ut_dulint_get_low(roll_ptr));
1395
trx_purge_sys_print();
1399
if (row_upd_changes_field_size_or_external(index, offsets, update)) {
1403
/* We have to set the appropriate extern storage bits in the
1404
old version of the record: the extern bits in rec for those
1405
fields that update does NOT update, as well as the the bits for
1406
those fields that update updates to become externally stored
1407
fields. Store the info to ext_vect: */
1409
ext_vect = mem_alloc(sizeof(ulint)
1410
* rec_offs_n_fields(offsets));
1411
n_ext_vect = btr_push_update_extern_fields(ext_vect, offsets,
1413
entry = row_rec_to_index_entry(ROW_COPY_DATA, index, rec,
1415
row_upd_index_replace_new_col_vals(entry, index, update, heap);
1417
buf = mem_heap_alloc(heap,
1418
rec_get_converted_size(index, entry));
1420
*old_vers = rec_convert_dtuple_to_rec(buf, index, entry);
1422
/* Now set the extern bits in the old version of the record */
1423
rec_set_field_extern_bits(*old_vers, index,
1424
ext_vect, n_ext_vect, NULL);
1427
buf = mem_heap_alloc(heap, rec_offs_size(offsets));
1428
*old_vers = rec_copy(buf, rec, offsets);
1429
rec_offs_make_valid(*old_vers, index, offsets);
1430
row_upd_rec_in_place(*old_vers, offsets, update);